NOIP 2015 子串 DP

来源:互联网 发布:tradingeconomics数据 编辑:程序博客网 时间:2024/05/22 13:06

这里写图片描述
n<=1000,m<=200,k<=m


今天是写这道题第2遍了…然而上次我还给别人讲了这道题的做法…现在就不会做了QAQ…


我们定义f[s][i][j]为使用了A串的前i个字符组成了s个非空子串匹配了B串的前j个字符的方案数…
我们可以很轻易的得出dp转移方程
f[s][i][j]+=f[s][i-1][j]+f[s-1][i-1][j-1] (a[i]==b[j])+f[s][i-1][j-1] (a[i]==b[j])
但是考虑这个转移方程会有重复的地方:
f[s][i-1][j]包含f[s-1][i-2][j-1],f[s-1][i-1][j-1]同样也包含f[s-1][i-2][j-1]…
所以f[s-1][i-2][j-1]被加了两次…所以我么就要减去一个f[s-1][i-2][j-1]…所以我们就不能滚i这一维只能滚s这一维了…


代码如下:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#define int long longusing namespace std;const int maxa=1000+5,maxb=200+5,p=1000000007;int n,m,k,f[2][maxa][maxb],tmp;char a[maxa],b[maxb];inline int read(){    char ch=getchar();    int f=1,x=0;    while(!(ch>='0'&&ch<='9')){        if(ch=='-')            f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')        x=x*10+ch-'0',ch=getchar();    return f*x;}signed main(void){    n=read(),m=read(),k=read(),tmp=0;    scanf("%s%s",a+1,b+1);    for(int i=0;i<=n;i++)        f[0][i][0]=1;    for(int s=1;s<=k;s++){        tmp=(tmp+1)%2;        for(int i=0;i<=n;i++)            f[tmp][i][s-1]=0;//因为我们选择了s个非空字串进行匹配,所以b中s-1个字符进行匹配的方案数当然是0         for(int j=s;j<=m;j++)            for(int i=j;i<=n;i++){                if(a[i]!=b[j])                    f[tmp][i][j]=f[tmp][i-1][j];                else{                    f[tmp][i][j]=((f[(tmp+1)%2][i-1][j-1]+f[tmp][i-1][j])%p+f[tmp][i-1][j-1])%p;                    if(i>=2)                        f[tmp][i][j]=(f[tmp][i][j]-f[tmp][i-2][j-1]+p)%p;                }            }    }    cout<<f[tmp][n][m]<<endl;    return 0;}

by >_< NeighThorn

1 0