NOIP2015子串题解
题目描述:
有两个仅包含小写英文字母的字符串 AA 和 BB。
现在要从字符串 AA 中取出 kk 个互不重叠的非空子串,然后把这 kk个子串按照其在字符串 AA 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 BB 相等?
注意:子串取出的位置不同也认为是不同的方案。
输入输出格式
输入格式:
第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问题描述中所提到的 k,每两个整数之间用一个空格隔开。
第二行包含一个长度为 n 的字符串,表示字符串 A。
第三行包含一个长度为 m 的字符串,表示字符串 B。
输出格式:
一个整数,表示所求方案数。
由于答案可能很大,所以这里要求输出答案对 1000000007 取模的结果。
解题思路:
f[i][j][k][0\1]表示A串前i位,B串前j位,选了k个字符,第i位选不选的方案数。
f[i][j][t][0]=f[i-1][j][t][0]+f[i-1][j][t][1]。
如果a[i]==b[j]
f[i][j][t][1]=f[i-1][j-1][t-1][1]+f[i-1][j-1][t-1][0]+f[i-1][j-1][t][1]。
如果a[i]!=b[j]
f[i][j][t][1]=0;
考虑滚动数组压维。
第一维可以通过滚动数组压掉。
也可以用01背包的逆序枚举。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define R register #define ll long long int using namespace std; const int N=1005; const int K=205; ll o=1; string a,b; ll n,m,k,mod=1000000007,ans,f[2][N][K][2];//从i到J选了k个串的方案 int main(){ scanf("%lld%lld%lld",&n,&m,&k); cin>>a;a=' '+a; cin>>b;b=' '+b; f[1][0][0][0]=1; f[0][0][0][0]=1; for(R ll i=1;i<=n;i++){ o=o^1; for(R ll j=1;j<=m;j++){ if(a[i]==b[j]) for(R ll t=1;t<=k;t++){ f[o][j][t][1]=(f[o^1][j-1][t-1][1]+(f[o^1][j-1][t][1]+f[o^1][j-1][t-1][0])%mod)%mod; f[o][j][t][0]=(f[o^1][j][t][0]+f[o^1][j][t][1])%mod; } else for(R ll t=1;t<=k;t++){ f[o][j][t][1]=0; f[o][j][t][0]=(f[o^1][j][t][0]+f[o^1][j][t][1])%mod; } } } printf("%lld",(f[o][m][k][0]+f[o][m][k][1])%mod); return 0; }