NKOI 3559 子串

原创 2016年05月30日 18:19:37

【NOIP2015 Day2】子串

Time Limit:10000MS  Memory Limit:131072K
Total Submit:2 Accepted:2 
Case Time Limit:1000MS

Description

        有两个仅包含小写英文字母的字符串A和B。现在要从字符串A中取出k个互不重叠的非空子串,然后把这k个子串按照其在字符串A中出现的顺序依次连接起来得到一个新的字符串,请问有多少种方案可以使得这个新串余字符串B相等?注意:子串取出的位置不同也认为是不同的方案。 

Input

        /*输入文件名为substring.in。*/ 
        第一行是三个正整数n,m,k,分别表示字符串A的长度,字符串B的长度,以及问题描述中所提到的k,每两个整数之间用一个空格隔开。 
        第二行包含一个长度为n的字符串,表示字符串A。 
        第三行包含一个长度为m的字符串,表示字符串B。 

Output

        /*输出文件名为substring.out。*/ 
        输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案1,000,000,007取模的结果。 

Sample Input

样例输入1:
6 3 1
aabaab
aab

样例输入2:
6 3 2
aabaab
aab

样例输入3:
6 3 3
aabaab
aab

Sample Output

样例输出1:
2

样例输出2:
7

样例输出3:
7

Hint

输入输出样例说明: 
所有合法方案如下:(加下划线的部分表示取出的子串) 
样例1:aab aab / aab aab 
样例2:a ab aab / a aba ab / a a ba ab / aab a ab / aa b aab / aa baa b / aab aa b 
样例3:a a b aab / a a baa b / a ab a a b / a aba a b / a a b a a b / a a ba a b / aab a a b 

数据规模与约定: 
        对于第1组数据:1<=n<=500, 1<=m<=50, k=1; 
        对于第2组至第3组数据:1<=n<=500, 1<=m<=50, k=2; 
        对于第4组至第5组数据:1<=n<=500, 1<=m<=50, k=m; 
        对于第1组至第7组数据:1<=n<=500, 1<=m<=50, 1<=k<=m; 
        对于第1组至第9组数据:1<=n<=1000, 1<=m<=100, 1<=k<=m; 
        对于所有10组数据:1<=n<=1000, 1<=m<=200, 1<=k<=m。 

Source

感谢nodgd手打题目


首先我们看的出来这是一道动归

状态:f[k][i][j]表示用A串中的前i个字母构成了k段,已经构成了B串的前j个字符的方案总数。
f[k][i][j]=
                   ∑ f[k-1][p][j-1]                              条件:A[i]==B[j]且A[i-1]!=B[j-1]     0<p<i 
                   ∑ f[k-1][p][j-1]+f[k][i-1][j-1]     条件:A[i]==B[j]且A[i-1]==B[j-1]     0<p<i 
第一个是A[i]独立开辟出一个段的方案数
第二个是把A[i]加入之前的第k段。算上之前就已经分了k个部分后,把第k个部分扩大的方案数
时间复杂度:O(nm2k)   显然会超时
观察发现,每次都要计算前缀状态和∑ dp[k-1][p][j-1]
我们可以用一个Sum[ ][ ][ ]数组来记录下来,避免重复计算,
Sum[k][i][j] = dp[k][i][j] + Sum[k][i-1][j];
时间复杂度可降至  O(nmk) 
我们观察发现dp[k][...][...]只跟dp[k-1][...][...]有关,滚动数组处理即可!
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mod=1000000007;
int K,n,m;
char a[1005],b[205];
int f[2][1005][205],sum[2][1005][205];
int main(){
	scanf("%d%d%d",&n,&m,&K);
	scanf("%s%s",a+1,b+1);
	int i,j,k;
	f[0][0][0]=1;
	for(i=0;i<=n;i++)
	    sum[0][i][0]=1;/////////初始化
	for(k=1;k<=K;k++){
		memset(sum[k&1],0,sizeof(sum[k&1]));
		memset(f[k&1],0,sizeof(f[k&1]));
		for(i=1;i<=n;i++)
		    for(j=1;j<=m;j++){
		    	if(a[i]==b[j]){
		    		f[k&1][i][j]=sum[(k-1)&1][i-1][j-1];
		    		if(a[i-1]==b[j-1])
		    		    f[k&1][i][j]=(f[k&1][i][j]+f[k&1][i-1][j-1])%mod;
				}
				sum[k&1][i][j]=(sum[k&1][i][j]+f[k&1][i][j]+sum[k&1][i-1][j])%mod;
			}
	}
	int ans=0;
	for(i=1;i<=n;i++)
	    ans=(ans+f[K&1][i][m])%mod;
	cout<<ans;
}




版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

查找子串程序

bzoj 3559: [Ctsc2014]图的分割 并查集

看到就猜是把边权从小到大排序然后插件去。。然而事实上排序后某条边必须插就插这条边是对的!        考虑一条边(x,y,z),x所在集合为u,y所在集合为v,其中u!=v;那么如果z     ...

hdu3559 Frost Chain (概率dp+记忆化搜索)

Problem Description In the unimaginable popular DotA game, the hero Lich has a wonderful skill: F...

寻找自由的钥匙 NKOI 树形DP

题目描述 通向自由的钥匙被放n个房间里,这n个房间由n-1条走廊连接。但是每个房间里都有特别的保护魔法,在它的作用下,我无法通过这个房间,也无法取得其中的钥匙。虽然我可以通过消耗能量来破坏房间...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)