【noip2015】【DP】子串

来源:互联网 发布:网络调试助手使用说明 编辑:程序博客网 时间:2024/06/05 21:06

子串

描述

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

格式

输入格式

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

输出格式

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

样例1

样例输入1

6 3 1
aabaab
aab

样例输出1

2

样例2

样例输入2

6 3 2
aabaab
aab

样例输出2

7

样例3

样例输入

6 3 3
aabaab
aab

样例输出3

7

限制

对于第 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。

提示

样例 1:aab aab / aab aab
样例 2:a ab aab / a aba ab / a a ba ab / aab a ab
aa b aab / aa baa b / aab aa b
样例 3: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

来源

NOIP 2015 提高组 Day 2 第二题

DP题,我竟然不会,QAQ
明明是个不复杂的DP我却想了好久,果然蒟蒻无比


先说一下滚动前的方程式
dp[0/1][i][j][k]表示dp到n的第i个字符,m的第j个字符,选了k个串有多少个情况,第一个0表示不选当前位,1表示选
然后方程式:

dp[0][i][j][k]=dp[0][i-1][j][k]+dp[1][i-1][j][k]

if(n[i]==m[j])dp[1][i][j][k]=dp[1][i-1][j-1][k]+dp[0][i-1][j-1][k-1]+dp[1][i-1][j-1][k-1]

故意写这么大因为我看小字就头晕,找到个字大的才看懂
然后一定要注意初始化,最复杂的一部分,不选第一位要初始化为n在i以前与m[1]相等的字符数量,如果n[i]==m[1],选就初始化为1
其他的看代码就可以懂了,我就不多说了

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<set>#include<map>#include<queue>#include<algorithm>#include<vector>#include<cstdlib>#include<cmath>#include<ctime>#include<stack>#define INF 2100000000#define ll long long#define clr(x)  memset(x,0,sizeof(x))#define maxclr(x)  memset(x,127,sizeof(x))using namespace std;inline int read(){    char c;    int ret=0;    while(!(c>='0'&&c<='9'))        c=getchar();    while(c>='0'&&c<='9')    {        ret=(c-'0')+(ret<<1)+(ret<<3);        c=getchar();    }    return ret;}#define M 1005#define N 205#define P 1000000007int dp[2][N][N],f[2][2][N][N];int m,n,k,t,sum;char sn[M],sm[N];void DP(){    for(int i=1;i<=n;i++)    {        for(int j=m;j>=2;j--)//倒着推为了滚动             for(int o=1;o<=k;o++)            {                dp[0][j][o]=(dp[0][j][o]+dp[1][j][o])%P;                if(sn[i]==sm[j])                    dp[1][j][o]=((dp[1][j-1][o]+dp[0][j-1][o-1])%P+dp[1][j-1][o-1])%P;                else dp[1][j][o]=0;            }        dp[0][1][1]=t;//初始化当前位前m第一位的情况         if(sn[i]==sm[1])//初始化当前位去第一位的情况         {            t++;            dp[1][1][1]=1;        }        else dp[1][1][1]=0;    }}int main(){    freopen("substring.in","r",stdin);    freopen("substring.out","w",stdout);    n=read();m=read();k=read();    scanf("%s\n%s",sn+1,sm+1);    DP();    printf("%d\n",(dp[0][m][k]+dp[1][m][k])%P);    return 0;}

大概就是这个样子,如果有什么问题,或错误,请在评论区提出,谢谢。

1 0
原创粉丝点击