題目連接:BZOJ - 5282php
LCS 就是用經典的 O(n^2) DP 解決,f[i][j] 表示 x 串前 i 個字符與 y 串前 j 個字符的 LCS 長度。ios
f[i][j] = max(f[i - 1][j], f[i][j - 1]); spa
if (x[i] == y[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);blog
而後再設置一個狀態 g[i][j], 表示 x 串的前 i 個字符中,有多少個長爲 f[i][j] 的子序列同時也是 y 串前 j 個字符的子序列。get
而後轉移的時候,分兩種狀況:string
1) 不包含 x[i] 的子序列, if (f[i - 1][j] == f[i][j]) g[i][j] += g[i - 1][j];it
2)包含 x[i] 的子序列,if (f[i - 1][p - 1] + 1 == f[i][j]) g[i][j] += g[i - 1][p - 1]; (p 是 y 串前 j 個字符中最靠後的與 x[i] 相同的位置。)io
這樣轉移就行了。class
初始化 g[][] 的時候給 g[0][] 和 g[][0] 賦值。stream
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; inline int gmax(int a, int b) {return a > b ? a : b;} const int MaxN = 1000 + 5, Mod = 1000000007; int T, n, m, Lp; int f[MaxN][MaxN], g[MaxN][MaxN]; char X[MaxN], Y[MaxN]; int main() { scanf("%d", &T); for (int Case = 1; Case <= T; ++Case) { scanf("%s%s", X + 1, Y + 1); n = strlen(X + 1); m = strlen(Y + 1); memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g)); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) { f[i][j] = gmax(f[i - 1][j], f[i][j - 1]); if (X[i] == Y[j]) f[i][j] = gmax(f[i][j], f[i - 1][j - 1] + 1); } g[0][0] = 1; for (int i = 1; i <= n; ++i) g[i][0] = 1; for (int i = 1; i <= m; ++i) g[0][i] = 1; for (int i = 1; i <= n; ++i) { Lp = 0; for (int j = 1; j <= m; ++j) { if (Y[j] == X[i]) Lp = j; if (f[i - 1][j] == f[i][j]) { g[i][j] += g[i - 1][j]; g[i][j] %= Mod; } if (Lp != 0 && f[i - 1][Lp - 1] + 1 == f[i][j]) { g[i][j] += g[i - 1][Lp - 1]; g[i][j] %= Mod; } } } printf("%d\n", g[n][m]); } return 0; }