P2516 [HAOI2010]最長公共子序列
阿新 • • 發佈:2020-09-09
P2516 [HAOI2010]最長公共子序列
題目連結
匹配DP。
最長公共子序列比較好求:
if(a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
else f[i][j] = max(f[i][j], max(f[i - 1][j], f[i][j - 1]));
那最長公共子序列的個數怎麼求呢?
我們用\(g[i][j]\)表示A串匹配到\(i\),B串匹配到\(j\)的最長上升子序列的個數。
if(a[i] == b[j]) { g[i][j] = g[i - 1][j - 1]; if(f[i][j] == f[i - 1][j]) g[i][j] += g[i - 1][j]; if(f[i][j] == f[i][j - 1]) g[i][j] += g[i][j - 1]; //f[i][j]肯定不等於f[i - 1][j - 1]; } else { g[i][j] = 0; if(f[i][j] == f[i - 1][j]) g[i][j] += g[i - 1][j]; if(f[i][j] == f[i][j - 1]) g[i][j] += g[i][j - 1]; if(f[i][j] == f[i - 1][j - 1]) g[i][j] -= g[i - 1][j - 1]; //如果第三個if成立,那麼第一個和第二個if也肯定成立,那就會多算了一部分,我們只需要把多算的那一部分減去就好了 }
記得用滾動陣列。
完整程式碼:
#include <bits/stdc++.h> using namespace std; inline long long read() { long long s = 0, f = 1; char ch; while(!isdigit(ch = getchar())) (ch == '-') && (f = -f); for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48)); return s * f; } const int N = 100005, mod = 100000000; char cha[N], chb[N]; int lena, lenb, f[2][N], g[2][N]; int main() { cin >> cha + 1; cin >> chb + 1; lena = strlen(cha + 1) - 1; lenb = strlen(chb + 1) - 1; for(int i = 0;i <= lenb; i++) g[0][i] = 1; g[1][0] = 1; for(int i = 1;i <= lena; i++) { int tmp = i & 1; for(int j = 1;j <= lenb; j++) if(cha[i] == chb[j]) { f[tmp][j] = max(f[tmp][j], f[tmp ^ 1][j - 1] + 1); g[tmp][j] = g[tmp ^ 1][j - 1]; if(f[tmp][j] == f[tmp ^ 1][j]) (g[tmp][j] += g[tmp ^ 1][j]) %= mod; if(f[tmp][j] == f[tmp][j - 1]) (g[tmp][j] += g[tmp][j - 1]) %= mod; } else { f[tmp][j] = max(f[tmp][j], max(f[tmp ^ 1][j], f[tmp][j - 1])); g[tmp][j] = 0; if(f[tmp][j] == f[tmp ^ 1][j]) (g[tmp][j] += g[tmp ^ 1][j]) %= mod; if(f[tmp][j] == f[tmp][j - 1]) (g[tmp][j] += g[tmp][j - 1]) %= mod; if(f[tmp][j] == f[tmp ^ 1][j - 1]) (g[tmp][j] -= g[tmp ^ 1][j - 1]) %= mod; } } printf("%d\n%d", f[lena & 1][lenb], (g[lena & 1][lenb] + mod) % mod); return 0; }