子串 [NOIP2015]

Description

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

Input

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

Output

输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求输出答案对 1,000,000,007 取模的结果。

Sample Input

样例输入1:
6 3 1
aabaab
aab

样例输入2:
6 3 2
aabaab
aab

Sample Output

样例输出1:
2

样例输出2:
7

Hint

数据范围:
对于第 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。

Solution

这道题一看tm就是道DP题,而且第一感觉会很复杂

设状态

首先先来观察,我们设状态的话,要存储的应该有当前位置i,被匹配到的位置j,用了几个子串k(dp[i][j][k])

但我们发现还有一个棘手的问题,如何判断当前的是一个独立的子串还是连接到前面的子串

那么我们就要知道某一个位置是否被匹配,再加上一维[0/1]代表是否被匹配

状态转移

对于dp[i][j][k][0],他没被匹配,那匹配位置没变,子串个数没变,从i-1转移就是 dp[i-1][j][k][0]+dp[i-1][j][k][1]

对于dp[i][j][k][1],前提条件就是A[i]==B[j],满足条件后我们发现,它可以分别从 (1)前一位没被匹配 (2)前一位被匹配,这个字符连接到前面一个子串 (3)前一位没被匹配,这一个字符单独成为一个子串,那么转移方程为dp[i-1][j-1][k-1][0]+dp[i-1][j-1][k-1][1]+dp[i-1][j-1][k][1]

细节&优化

边界条件:dp[i][1][1][0]=sigma(j=1->i-1)(A[j]==B[1])(显然可以累计,不需要每次求)  dp[i][1][1][1]=(A[i]==B[1])

dp[1000][200][200][2]的空间是显然爆炸的,由于只要用到前一维,可以滚一滚第一维

而每次计算完dp[i]后,要清空dp[i-1],否则会对dp[i+1]产生干扰(因为是滚动的)

Code

 1 #include<set>
 2 #include<map>
 3 #include<stack>
 4 #include<queue>
 5 #include<cstdio>
 6 #include<cstring>
 7 #include<iostream>
 8 #include<algorithm>
 9 #define RG register int
10 #define rep(i,a,b)    for(RG i=a;i<=b;i++)
11 #define per(i,a,b)    for(RG i=a;i>=b;i--)
12 #define ll long long
13 #define inf (1<<30)
14 #define maxm 205
15 using namespace std;
16 const ll mo=1000000007;
17 int n,m,t,p,q;
18 ll sum;
19 ll dp[2][maxm][maxm][2];
20 char A[1005],B[maxm];
21 inline int read()
22 {
23     int x=0,f=1;char c=getchar();
24     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
25     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
26     return x*f;
27 }
28 
29 int main()
30 {
31     n=read(),m=read(),t=read();
32     scanf("%s",A+1);scanf("%s",B+1);
33     p=1,q=0;
34     rep(i,1,n)
35     {
36         swap(p,q);
37         dp[p][1][1][0]=sum;
38         if(A[i]==B[1])    dp[p][1][1][1]=1,sum++;
39         rep(j,2,m)
40             rep(k,1,t)
41             {
42                 if(A[i]==B[j])    (dp[p][j][k][1]=(dp[q][j-1][k-1][0]+dp[q][j-1][k][1])%mo+dp[q][j-1][k-1][1])%=mo;            
43                 dp[p][j][k][0]=(dp[q][j][k][0]+dp[q][j][k][1])%mo;
44             }
45        rep(j,1,m)    rep(k,1,t)    dp[q][j][k][0]=dp[q][j][k][1]=0;
46     }
47     cout<<(dp[p][m][t][0]+dp[p][m][t][1])%mo;
48     return 0;
49 }
View Code
原文地址:https://www.cnblogs.com/ibilllee/p/7726140.html