【洛谷P2679】子串

题目

题目链接: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;
}
posted @ 2020-09-22 19:00  stoorz  阅读(98)  评论(0)    收藏  举报