【NOIP2015提高组】子串

时间:2023-11-22 11:53:44

https://daniu.luogu.org/problem/show?pid=2679

看到方案数问题直觉就能想到DP,考虑用f(i,j,k)表示A[1...i]取k个子串组成B[1...j]的方案数,发现很难转移,因为不知道之前的方案哪些是还能拼接到结尾的,产生了前效性。

考虑加一维,即

A[1...i]取k个子串组成B[1...j],且末尾子串还可以继续拼接的方案数为:f(i,j,k,0)={
    拼接上一个子串f(i-1,j-1,k,0)+另开新串f(i-1,j-1,k-1,1) (A[i]==B[j]),
    0 (A[i]!=B[j])
}

A[1...i]取k个子串组成B[1...j],且末尾子串已经封闭或末尾根本不是子串的方案数为:f(i,j,k,1)=sum{
    拼接上一个子串然后封闭f(i-1,j-1,k,0) (A[i]==B[j]),
    开一个字符的串f(i-1,j-1,k-1,1) (A[i]==B[j]),
    不用这个字符f(i-1,j,k,1)
}
其实就是f(i,j,k,1)=用这个字符然后封闭f(i,j,k,0)+不用这个字符f(i-1,j,k,1)

特别的,f(i,0,0,1)=1

#include <iostream>
#include <string>
using namespace std;
typedef long long llint;
llint n, m, kk;
string a, b;
const llint c = 1e9 + ;
llint dp[][][][];
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m >> kk >> a >> b;
// f(i,j,k,0) = a[i]==b[j] ? f(i-1,j-1,k,0)+f(i-1,j-1,k-1,1) : 0
// f(i,j,k,1) = f(i,j,k,0) + f(i-1,j,k,1)
dp[][][][] = dp[][][][] = ; // f(i,0,0,1)=1
for (int i = ; i <= n; i++)
{
for (int j = ; j <= m; j++)
{
for (int k = ; k <= kk; k++)
{
dp[i & ][j][k][] = (a[i - ] == b[j - ]) ? dp[(i - ) & ][j - ][k][] + dp[(i - ) & ][j - ][k - ][] : ;
dp[i & ][j][k][] = dp[i & ][j][k][] + dp[(i - ) & ][j][k][]; while (dp[i & ][j][k][] >= c)
dp[i & ][j][k][] -= c;
while (dp[i & ][j][k][] >= c)
dp[i & ][j][k][] -= c;
}
}
}
cout << dp[n & ][m][kk][] << endl;
return ;
}