题解 洛谷 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;
}
posted @ 2020-10-24 20:08  Little09  阅读(154)  评论(0)    收藏  举报