UOJ 149 子串

来源:互联网 发布:suse linux samba 编辑:程序博客网 时间:2024/06/17 13:38

#149. 【NOIP2015】子串

 统计

有两个仅包含小写英文字母的字符串 AA 和 BB

现在要从字符串 AA 中取出 kk 个互不重叠的非空子串,然后把这 kk 个子串按照其在字符串 AA 中出现的顺序依次连接起来得到一个新的字符串。请问有多少种方案可以使得这个新串与字符串 BB 相等?

注意:子串取出的位置不同也认为是不同的方案。

输入格式

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

第二行包含一个长度为 nn 的字符串,表示字符串 AA

第三行包含一个长度为 mm 的字符串,表示字符串 BB

输出格式

输出共一行,包含一个整数,表示所求方案数。

由于答案可能很大,所以这里要求输出答案对 10000000071000000007 取模的结果。

样例一

input

6 3 1aabaabaab

output

2

样例二

input

6 3 2aabaabaab

output

7

样例三

input

6 3 3aabaabaab

output

7

explanation

所有合法方案如下:(加下划线的部分表示取出的子串)

样例一:aab aab / aab aab

样例二:a ab aab / a aba ab / a a ba ab / aab a ab / aa b aab / aa baa b / aab aa b

样例三:a a b aab / a a baa b / a ab a a b / a aba a b / a a b a a b / a a ba a b / aab a a b

限制与约定

测试点编号nn的规模mm的规模kk的规模1n500n≤500m50m≤50k=1k=12k=2k=234k=mk=m56kmk≤m78n1000n≤1000m100m≤100910m200m≤200

时间限制:1s1s

空间限制:128MB


看过去就知道是DP啊!!本题超多细节高能。

设f[i][j][k][0/1]表示A串的前i位取出k个子串匹配了B串的前j位,选或不选第i位。

分两种情况1.若A串第i位与B串第j位匹配,那么f[i][j][k][1]=f[i-1][j-1][k][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1].

解释一下:若匹配,则选择这一位的情况有如下几种累加方式:(首先B串的匹配个数都要-1)前一位也选并与当前位构成同一个子串(子串个数不变)、前一位选并且不断点为这一位、前一位不选(子串个数都要-1)。

f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1].  解释一下:若不选择这一位,当前方式可以累加的有:(B串的匹配个数不变)前一位选或者前一位不选,子串个数不变(因为这一位不选)

2.若A串第i位与B串第j位不匹配,则f[i][j][k][0]=f[i-1][j][k][0]+f[i-1][j][k][1].解释:若不匹配,该位肯定不能选。所以不选的方案数累加上前一位选或者不选的个数。


#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<iostream>#include<cmath>using namespace std;const int MAXN=1010;const int MAXM=220;const int Mod=1e9+7;long long f[2][MAXM][MAXM][2];int main(){char a[MAXN],b[MAXM];int n,m,k,cnt=0,last=-1;scanf("%d%d%d",&n,&m,&k);scanf("%s",a+1);scanf("%s",b+1);f[0][0][0][0]=1;for (int i=1;i<=n;i++){cnt++;cnt%=2;last++;last%=2;for (int j=0;j<=m;j++){for (int p=0;p<=k;p++){if (a[i]==b[j]){if (j>0){if (p>0){f[cnt][j][p][1]=f[last][j-1][p][1]+f[last][j-1][p-1][0]+f[last][j-1][p-1][1];f[cnt][j][p][1]%=Mod;}}f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];f[cnt][j][p][0]%=Mod;}else{f[cnt][j][p][0]=f[last][j][p][0]+f[last][j][p][1];f[cnt][j][p][0]%=Mod;}}}for (int j=0;j<=m;j++){for (int p=0;p<=k;p++){f[last][j][p][0]=0;f[last][j][p][1]=0;}}}cout<<(f[n%2][m][k][1]+f[n%2][m][k][0])%Mod;    return 0;}
需要开滚动数组不然会MLE,滚动数组每用完一次后都要把当前清零。

再次吐槽超多细节(NOIP还是坑人啊)


原创粉丝点击