【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-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
我们可以用一个Sum[ ][ ][ ]数组来记录下来,避免重复计算,
Sum[k][i][j] = dp[k][i][j] + Sum[k][i-1][j];
时间复杂度可降至 O(nmk)
#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;
}