正常的LCS问题,时间复杂度是O(|A|*|B|)
但是这道题有一个特点:B串的长度很短,小于等于1000
所以可以换一个状态记录:f[i][j]为A串匹配到第i位,最长公共子序列长度为j的最靠左的B串的位置
为了递推这个方程,需要预处理一个nxt[i][j]表示当前B串在i位置,下一个匹配到j的位置
这样时间复杂度就是O(|A|)
代码
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 1010, inf = 0x7ffffff; int f[maxn][maxn], nextt[100010][30], n, m, ans; char a[1010], b[100010]; int main() { freopen("lcs.in","r",stdin); freopen("lcs.out","w",stdout); scanf("%s%s", a, b); n = strlen(a); m = strlen(b); for (int i = 0; i <= n; i++) for (int j = 0; j <= n; j++) f[i][j] = inf; for (int i = 0; i < 26; i++) nextt[m][i] = inf; for (int i = m - 1; i >= 0; i--) { memcpy(nextt[i], nextt[i + 1], sizeof(nextt[i])); nextt[i][b[i] - 'a'] = i; } f[0][1] = nextt[0][a[0] - 'a']; for (int i = 0; i <= n; i++) f[i][0] = -1; for (int i = 0; i < n - 1; i++) for (int j = 0; j <= n && f[i][j] < inf; j++) { f[i + 1][j] = min(f[i + 1][j], f[i][j]); if (j < n) f[i + 1][j + 1] = min(f[i + 1][j + 1], nextt[f[i][j] + 1][a[i + 1] - 'a']); } for (int i = n; i >= 1; i--) if (f[n - 1][i] != inf) { ans = i; break; } printf("%d\n", ans); return 0; }