【NOIP 2015 提高组】子串

传送门


problem

有两个仅包含小写英文字母的字符串 A A A(长度为 n n n) 和 B B B(长度为 m m m)。

现在要从字符串 A A A 中取出 k k k 个互不重叠的非空子串,然后把这 k k k 个子串按照其在字符串 A A A 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 B B B 相等?

答案对 1000000007 1000000007 1000000007 取模

注意:子串取出的位置不同也认为是不同的方案。

数据范围: 1 ≤ n ≤ 1000 1≤n≤1000 1n1000 1 ≤ k ≤ m ≤ 200 1≤k\le m≤200 1km200


solution

这道题应该很容易看出是 d p dp dp 吧。

我们设 f [ i ] [ j ] [ k ] [ 0 / 1 ] f[i][j][k][0/1] f[i][j][k][0/1] 表示在 A A A 的前 i i i 个字符中选 k k k 个子串拼成 B B B 的前 j j j 和字符,且第 i i 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 ] + f [ i − 1 ] [ j − 1 ] [ k − 1 ] [ 1 ] A [ i ] = B [ j ] 0 A [ i ] ≠ B [ j ] \begin{aligned} f[i][j][k][0]&=f[i-1][j][k][0]+f[i-1][j][k][1]\\ f[i][j][k][1]&=\begin{cases} f[i-1][j-1][k-1][0]+f[i-1][j-1][k][1]+f[i-1][j-1][k-1][1]&A[i]=B[j]\\ 0 &A[i]\ne B[j] \end{cases} \end{aligned} f[i][j][k][0]f[i][j][k][1]=f[i1][j][k][0]+f[i1][j][k][1]={f[i1][j1][k1][0]+f[i1][j1][k][1]+f[i1][j1][k1][1]0A[i]=B[j]A[i]=B[j]

算一下空间,发现 1000 × 20 0 2 × 2 1000\times200^2\times 2 1000×2002×2 要炸,但是注意到 f [ i ] f[i] f[i] 的转移只与 f [ i − 1 ] f[i-1] f[i1] 有关,可以用滚动数组优化掉第一维。

时间复杂度 O ( n m k ) O(nmk) O(nmk)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define P 1000000007
using namespace std;
int n,m,K,t=1;
int f[2][205][205][2];
char A[1005],B[205];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int main(){
	scanf("%d%d%d",&n,&m,&K);
	scanf("%s%s",A+1,B+1);
	f[0][0][0][0]=1;
	for(int i=1;i<=n;++i){
		f[t][0][0][0]=1;
		for(int j=1;j<=m;++j){
			for(int k=1;k<=m;++k){
				f[t][j][k][0]=add(f[t^1][j][k][0],f[t^1][j][k][1]);
				f[t][j][k][1]=(A[i]==B[j])?add(add(f[t^1][j-1][k][1],f[t^1][j-1][k-1][1]),f[t^1][j-1][k-1][0]):0;
			}
		}
		t^=1;
	}
	printf("%d\n",add(f[t^1][m][K][0],f[t^1][m][K][1]));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值