NOIP2015 子串 (DP+优化)

子串
(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 }
原文地址:https://www.cnblogs.com/lidaxin/p/5215282.html