Luogu P2516 [HAOI2010]最长公共子序列 DP

首先$LIS$显然:$f[i][j]=max(f[i][j-1],f[i-1][j],(a[i]==b[j])*f[i-1][j-1])$

考虑如何转移数量:

首先,不管$a[i]$是否等于$b[j]$,

都有$h[i][j]+=h[i-1][j]*(f[i][j]==f[i-1][j])+h[i][j-1]*(f[i][j]==f[i][j-1])$

然后讨论$LIS$中第三种转移:

如果$a[i]==b[j] && f[i][j]==f[i-1][j-1]+1$,有$h[i][j]+=h[i-1][j-1]$

而如果$a[i]!=b[j] && f[i][j]==f[i-1][j-1]$,有$h[i][j]-=h[i-1][j-1]$,此处是考虑$h[i-1][j]$与$h[i][j-1]$对答案的重复转移,所以要减去;

#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
namespace Fread {
    static char B[1<<15],*S=B,*D=B;
    #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin),S==D)?EOF:*S++)
    inline int g() {
        R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
        do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
    } inline bool isempty(const char& ch) {return ch<=36||ch>=127;}
    inline void gs(char* s) {register char ch; while(isempty(ch=getchar())); do *s++=ch; while(!isempty(ch=getchar()));}
}using Fread::g; using Fread::gs;
const int N=500010,M=1E+8;
int n,m; char s1[N],s2[N];
int f[2][N],h[2][N];
signed main() {
    gs(s1+1),gs(s2+1); R l1=strlen(s1+1)-1,l2=strlen(s2+1)-1;
    R c=0; for(R i=0;i<=l2;++i) h[c][i]=1;
    for(R i=1;i<=l1;++i) {
        c^=1; h[c][0]=1;
        for(R j=1;j<=l2;++j) {
            h[c][j]=0;
            f[c][j]=max(f[c^1][j],f[c][j-1]);
            if(s1[i]==s2[j]) f[c][j]=max(f[c][j],f[c^1][j-1]+1);
            if(f[c][j]==f[c^1][j]) h[c][j]+=h[c^1][j];
            if(f[c][j]==f[c][j-1]) h[c][j]+=h[c][j-1];
            if(s1[i]==s2[j]&&f[c][j]==f[c^1][j-1]+1) h[c][j]+=h[c^1][j-1];
            if(s1[i]!=s2[j]&&f[c][j]==f[c^1][j-1]) h[c][j]-=h[c^1][j-1];
            h[c][j]=(h[c][j]+M)%M;
        }
    } printf("%d
%d
",f[c][l2],h[c][l2]);
}

2019.07.11

原文地址:https://www.cnblogs.com/Jackpei/p/11170734.html