2015提高组 子串
题目描述
有两个仅包含小写英文字母的字符串 AA 和 BB。
现在要从字符串 AA 中取出 kk 个互不重叠的非空子串,然后把这 kk 个子串按照其在字符串 AA 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 BB 相等?
注意:子串取出的位置不同也认为是不同的方案。
输入输出格式
输入格式:
第一行是三个正整数 n,m,kn,m,k,分别表示字符串 AA 的长度,字符串 BB 的长度,以及问题描述中所提到的 kk,每两个整数之间用一个空格隔开。
第二行包含一个长度为 nn 的字符串,表示字符串 AA。
第三行包含一个长度为 mm 的字符串,表示字符串 BB。
输出格式:
一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 10000000071000000007 取模的结果。
lv神Day6T2,一个爆水的dp,呃,然而净顾着写T1,还忘了删注释
首先dp方程是好推的,dp[i][j][l][0]代表主串到i,模式串到j,分了l段,当前位可取也可不取
dp[i][j][l][1]代表主串到i,模式串到j,分了l段,当前位必须取
dp[i][j][l][1]=dp[i-1][j-1][l][1]+dp[i-1][j-1][l-1][0]
dp[i][j][l][0]=dp[i][j][l][1]+dp[i-1][j][l][0]
然后就发现MLE了,怎么解决呢?
我们将这个dp数组看做一个网格图
每一个dp[i][j][l][0],都是从他的左上角和他的上方的两个方格处转移过来的
所以我们将循环倒过来,这样就可以压缩我们的状态,提前计算出要处理的方格的上面的方格,再从前面的方格转移
如下:
dp[j][l][1]=dp[j-1][l][1]+dp[j-1][l-1][0]
dp[j][l][0]=dp[j][l][1]+dp[j][l][0]
然后就是基础操作
下面给出代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> using namespace std; inline int rd(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return ; } int dp[1006][206][2]; char a[100006]; char b[100006]; int n,m,k; int mod=1000000007; int main(){ n=rd(); m=rd(); k=rd(); scanf("%s%s",a+1,b+1); dp[0][0][0]=1; for(int i=1;i<=n;i++){ for(int j=m;j>=1;j--){ if(a[i]==b[j]){ for(int v=k;v>=1;v--){ dp[j][v][1]=(dp[j-1][v-1][0]+dp[j-1][v][1])%mod; dp[j][v][0]=(dp[j][v][1]+dp[j][v][0])%mod; } } else for(int v=1;v<=k;v++) dp[j][v][1]=0; } } printf("%d",dp[m][k][0]); return 0; }