洛谷P2679 [NOIP2015]子串

毒瘤dp

思维量巨大,于是我抄了题解

我们先写一个假的dp转移方程:f[i][j][k]表示a串到第i位,b串到第j位,一共用了a中的k个子串的方案数, 咋转移?考虑当前考虑的a的这位是单独成一个串还是与前面的连在一起于是乎就有了f[i][j][k]=f[i-1][j-1][k]+f[i-1][j-1][k-1],前提条件是a[i]=b[j],但如果不相等呢?那这个方程就出锅了,所以我们新考虑了一个东西:当前位选还是不选,显然,对于这两种情况无法在同一个dp数组内表达出来,所以我们定义

g[i][j][k]表示a到第i位,b到第j位,已经用了k个子串,且必须要选上a[i]的方案数

f[i][j][k]表示a到第i位,b到第j位,已经用了k个子串,当前a[i]可以选也可以不选的方案数

然后我们看一下如何转移

对于g[i][j][k],a[i]必须选上,那么它可以和前面的连在一起,也可以单独成为一个串,单独成为一个串的情况,就无需考虑前一位是否选了,所以是f[i-1][j-1][k-1],若与前面的连在一起,那么前一位必须选,就是g[i-1][j-1][k],所以,g[i][j][k]=f[i-1][j-1][k-1]+g[i-1][j-1][k]

对于f[i][j][k],a[i]可选可不选,对于选它,那么对应的就是g[i][j][k]的方案数,对于不选它,那么就是f[i-1][j][k],所以得到f[i][j][k]=f[i-1][j][k]+g[i][j][k]

然后我们就dp完了

还有一个严肃的问题就是数组开不下,那就滚动一维

代码

//By AcerMo
#include
#include
#include
#include
#include
using namespace std;
const int mod=1e9+7;
int n,m,e;
char a[1050],b[1050];
int f[1050][1050][3],g[1050][1050][3];
signed main()
{
    scanf("%d%d%d",&n,&m,&e);
    scanf("%s",a+1);scanf("%s",b+1);
    int x=1,y=0;f[0][0][0]=1;
    for (int i=1;i<=n;swap(x,y),i++)
    {
        f[0][0][x]=1;
        for (int k=1;k<=m;k++)
        for (int j=1;j<=e;j++)
        {
            if (a[i]==b[k]) g[k][j][x]=(g[k-1][j][y]+f[k-1][j-1][y])%mod;
            else g[k][j][x]=0;
            f[k][j][x]=(f[k][j][y]+g[k][j][x])%mod;
        }
    }
    cout<return 0;
}

你可能感兴趣的:(动态规划)