题目
题目链接:https://www.luogu.com.cn/problem/P2679
有两个仅包含小写英文字母的字符串 \(A\) 和 \(B\)。
现在要从字符串 \(A\) 中取出 \(k\) 个互不重叠的非空子串,然后把这 \(k\) 个子串按照其在字符串 \(A\) 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 \(B\) 相等?
注意:子串取出的位置不同也认为是不同的方案。
思路
设 \(f[i][j][k][0/1]\) 表示 \(A\) 串前 \(i\) 个位置,匹配了 \(k\) 次匹配到 \(B\) 串前 \(j\) 位,其中第 \(i\) 位选或不选的方案数。
那么显然有
\[f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1]
\]
\[f[i][j][k][1]=f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1]+f[i-1][j-1][k][1]
\]
发现空间过不了,将 \(i\) 这一位滚动一下即可。
时间复杂度 \(O(nmk)\),空间复杂度 \(O(mk)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1010,M=210,MOD=1000000007;
int n,m,t,f[2][M][M][2];
char a[N],b[M];
int main()
{
scanf("%d%d%d",&n,&m,&t);
scanf("%s%s",a+1,b+1);
f[0][0][0][0]=1;
for (int i=1;i<=n;i++)
{
int id=i&1;
memset(f[id],0,sizeof(f[id]));
for (int j=0;j<=m;j++)
for (int k=0;k<=t;k++)
{
f[id][j][k][0]=(f[id^1][j][k][0]+f[id^1][j][k][1])%MOD;
if (j && a[i]==b[j])
{
if (k) f[id][j][k][1]=(f[id^1][j-1][k-1][0]+f[id^1][j-1][k-1][1])%MOD;
f[id][j][k][1]=(f[id][j][k][1]+f[id^1][j-1][k][1])%MOD;
}
}
}
printf("%d",(f[n&1][m][t][0]+f[n&1][m][t][1])%MOD);
return 0;
}