[HAOI2010] 最长公共子序列 - dp

只愿长相守 提交于 2020-03-01 16:31:55

求两个字符序列的最长公共子序列以及个数,\(n\leq 5000\)

Solution

第一问,考虑 \(f[i][j]\) 表示两个串分别跑到了 \(i,j\) 位置的最长公共子序列,则
\[ f[i][j]=\max(f[i-1][j],f[i][j-1],f[i-1][j-1]+[s[i]==t[j]]) \]
暴力转移即可

第二问,考虑 \(g[i][j]\) 表示两个串分别跑到了 \(i,j\) 位置的最长公共子序列的方案数,则先正常统计从 \(f[i-1][j],f[i][j-1]\) 过来的方案数,然后考虑两种特殊情况

  • 如果 \(f[i][j]=f[i-1][j-1]\) 并且 \(s[i]\neq t[j]\),那么需要额外减去 \(g[i-1][j-1]\)
  • 如果 \(f[i][j]=f[i-1][j-1]+1\) 并且 \(s[i]=t[j]\),那么需要额外加上 \(g[i-1][j-1]\)
#include <bits/stdc++.h>
using namespace std;
const int mod = 100000000;
int n,m,f[2][5005],g[2][5005];
char s[5005],t[5005];

signed main() {
    cin>>s+1>>t+1;
    n=strlen(s+1);
    m=strlen(t+1);
    --n; --m;
    for(int i=0;i<=n;i++) g[0][i]=1;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) g[i&1][j]=0;
        g[i&1][0]=1;
        for(int j=1;j<=m;j++) {
            f[i&1][j]=max(max(f[i-1&1][j],f[i&1][j-1]),
                        f[i-1&1][j-1]+(s[i]==t[j]));
            if(f[i&1][j]==f[i-1&1][j]) (g[i&1][j]+=g[i-1&1][j])%=mod;
            if(f[i&1][j]==f[i&1][j-1]) (g[i&1][j]+=g[i&1][j-1])%=mod;
            if(f[i&1][j]==f[i-1&1][j-1]&&s[i]!=t[j])
                (g[i&1][j]+=mod-g[i-1&1][j-1])%=mod;
            if(f[i&1][j]==f[i-1&1][j-1]+1&&s[i]==t[j])
                (g[i&1][j]+=g[i-1&1][j-1])%=mod;
        }
    }
    cout<<f[n&1][m]<<"\n"<<g[n&1][m]<<endl;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!