1. 程式人生 > >【LCS】BZOJ2423(HAOI2010)[最長公共子序列]題解

【LCS】BZOJ2423(HAOI2010)[最長公共子序列]題解

題目概述

求兩個字串 AB 的最長公共子序列以及最長公共子序列的數量。

解題報告

定義 f[i][j] 表示 A 的前 i 位與 B 的前 j 位的最長公共子序列,g[i][j] 表示最長公共子序列的方案數。第一問沒話說,第二問要注意重複的情況:

Ai=Bj 時,直接從三個狀態轉移,累加方案數。

AiBj 時,從 f[i][j1]f[i1][j] 轉移,如果 f[i1][j1]=f[i][j1]=

f[i1][j] ,那麼說明 g[i1][j1] 算重複了 1 次,減去。

注意會MLE(別問我為什麼知道QAQ),所以用滾動陣列。

示例程式

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=5000,maxm=5000,MOD=100000000;

int n,m,f[2][maxm+5],g[2][maxm+5];char A[maxn+5],B[maxm+5];

inline void AMOD(int &x,int tem) {if ((x+=tem)>=MOD) x-=MOD;}
inline
void Fix(int &f,int &g,int a,int b) {if (f==a) return AMOD(g,b);if (a>f) f=a,g=b;} int main(){ freopen("program.in","r",stdin); freopen("program.out","w",stdout); scanf("%s%s",A+1,B+1);n=strlen(A+1)-1;m=strlen(B+1)-1; for (int j=0;j<=m;j++) g[0][j]=1; for (int i=1
,c=1;i<=n;i++,c^=1){ memset(f[c],0,sizeof(f[c]));memset(g[c],0,sizeof(g[c]));g[c][0]=1; for (int j=1;j<=m;j++){ Fix(f[c][j],g[c][j],f[c^1][j],g[c^1][j]); Fix(f[c][j],g[c][j],f[c][j-1],g[c][j-1]); if (A[i]==B[j]) Fix(f[c][j],g[c][j],f[c^1][j-1]+1,g[c^1][j-1]); else if (f[c^1][j-1]==f[c^1][j]&&f[c^1][j-1]==f[c][j-1]) AMOD(g[c][j],MOD-g[c^1][j-1]); } } return printf("%d\n%d\n",f[n&1][m],g[n&1][m]),0; }