子串
(substring.cpp/c/pas)
【问题描述】 有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个 互不重 叠 的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。
【输入格式】 输入文件名为 substring.in。 第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问 题描述中所提到的 k,每两个整数之间用一个空格隔开。 第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。
【输出格式】 输出文件名为 substring.out。 输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输 出答案对 1,000,000,007 取模的结果。
【思路】
DP+优化
设f[k][i][j]为已经有k段,A串匹配到i,B匹配到j的方案数,则有转移式:
f[k][i][j]=sigma{f[k-1][l][j-1]},A[i]==B[j]&&A[i-1]!=B[j-1]
= sigma{f[k-1][l][j-1]}+f[k][i-1][j-1],A[i]==B[j]&&A[i-1]==B[j-1]
前缀和优化时间,滚动数组优化空间。
【代码】
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 const int N = 1e3+5; 6 const int M = 200+5; 7 const int MOD = 1e9+7; 8 9 int f[2][N][M],sum[2][N][M],n,m,K; 10 char s1[N],s2[M]; 11 12 int main() { 13 scanf("%d%d%d",&n,&m,&K); 14 scanf("%s",s1+1),scanf("%s",s2+1); 15 f[0][0][0]=1; 16 for(int i=0;i<=n;i++) sum[0][i][0]=1; 17 int x=0; 18 for(int k=1;k<=K;k++) { 19 x^=1; 20 memset(sum[x],0,sizeof(sum[x])); 21 memset(f[x],0,sizeof(f[x])); 22 for(int i=1;i<=n;i++) 23 for(int j=1;j<=m;j++) { 24 if(s1[i]==s2[j]) { 25 f[x][i][j]=sum[x^1][i-1][j-1]; 26 if(s1[i-1]==s2[j-1]) f[x][i][j]=(f[x][i][j]+f[x][i-1][j-1])%MOD; 27 } 28 sum[x][i][j]=((sum[x][i][j]+sum[x][i-1][j])%MOD+f[x][i][j])%MOD; 29 } 30 } 31 int ans=0; 32 for(int i=1;i<=n;i++) 33 ans=(ans+f[x][i][m])%MOD; 34 printf("%d",ans); 35 return 0; 36 }