题解 洛谷 P2679 【子串】
一道区间dp。
状态很好设,我们可以设 \(dp(i,j,k)\) 表示在 \(a\) 的第 \(i\) 位前取出 \(k\) 个子串与 \(b\) 的前 \(j\) 位一样的方案数。然后发现不是很好转移。
这个时候很容易就想到,我们再设一维0/1,即 \(dp(i,j,k,t)\),\(t\)表示 \(a\) 的第 \(i\) 位是否取到了。这样就可以分类讨论转移了。
因为我做的时候不会转移(太菜了)。 所以重点说转移。
首先,若 \(a_i!=b_j\),那么说明 \(a\) 的第 \(i\) 位不可能取到了,因此匹配不了,\(dp(i,j,k,1)\) 肯定为 \(0\) 了。而 \(dp(i,j,k,0)\) 由于 \(a\) 的第 \(i\) 为不参与,那么肯定就等于到上一位的结果,即 \(dp(i-1,j,k,0)+dp(i-1,j,k,1)\)。
接下来讨论 \(a_i=b_j\) 的情况。\(dp(i,j,k,0)\) 显然和上面是一样的,即 \(dp(i-1,j,k,0)+dp(i-1,j,k,1)\)。但是 \(dp(i,j,k,1)\) 怎么考虑呢?如果 \(a\) 的第 \(i\) 位是单独算的(即不和之前的一位组成子串),那方案数是 \(dp(i,j-1,k-1,0)\)。就是到这一位不选,选 \(k-1\) 的子串(这好理解吧)。如果 \(a\) 的第 \(i\) 位不是单独算的,也就是说和之前的拼接起来,那么就是 \(a\) 的 \(i-1\) 位和 \(b\) 的 \(j-1\) 位一样才可以拼接,所以这里方案数是 \(dp(i-1,j-1,k,1)\)。那么 \(dp\) 方程就推出来了。
最后由于内存显然不够,每个之和 \(i-1\) 有关,滚动一下 \(dp\) 数组就好。还有,初始化 \(dp(i,0,0,0)=1\),否则就全是 \(0\) 啦。
写了这么多,感觉有点啰嗦,希望您能看得懂qwq。最后贴个代码。
#include<bits/stdc++.h>
using namespace std;
int n,m,K;
char a[1201],b[1201];
int mod=1000000007;
long long f[2][201][201][2];
int main()
{
cin >> n >> m >> K;
for (int i=1;i<=n;i++) cin >> a[i];
for (int i=1;i<=m;i++) cin >> b[i];
f[0][0][0][0]=1;
for (int i=1;i<=n;i++)
{
f[i%2][0][0][0]=1;
for (int j=1;j<=m;j++)
{
for (int k=1;k<=min(j,K);k++)
{
if (a[i]!=b[j])
{
f[i%2][j][k][0]=(f[(i-1)%2][j][k][0]+f[(i-1)%2][j][k][1])%mod;
f[i%2][j][k][1]=0;
}
else
{
f[i%2][j][k][0]=(f[(i-1)%2][j][k][0]+f[(i-1)%2][j][k][1])%mod;
f[i%2][j][k][1]=(f[i%2][j-1][k-1][0]+f[(i-1)%2][j-1][k][1])%mod;
}
}
}
}
cout << ((f[n%2][m][K][0]+f[n%2][m][K][1])%mod) << endl;
return 0;
}