求S串和T串的最長公共子串,不一樣的是S串中有k個區間的字母能夠重拍,且不限次數。node
容易發現區間交叉能夠合併,這樣最多有2000個區間。
而後dp。
設f[i][j]爲以S串的第i個區間開頭,以T串的第j個字母開頭的最長公共子串。
對於區間i,判斷是否能夠與[j~j+len)匹配(前綴和優化)。
若是所有匹配,則f[i][j] = len + f[i + 1][j + len](便可以與i+1的區間接上);
不然f[i][j] = len(沒法接上)。
另外對於左邊,可能有部分的子串能夠匹配,暴力判斷便可。ide
#include <cstdio> #include <cstring> #include <algorithm> struct node { int l, r, len; }a[100002], b[2001]; int m, n, k, cnt, ans; int f[2001][2001], pre[2001][27], tmp[27]; char t[2001], s[2001]; bool cmp(node x, node y) { return x.l == y.l ? x.r > y.r : x.l < y.l; } int main() { freopen("lcs.in", "r", stdin); freopen("lcs.out", "w", stdout); scanf("%s", t + 1); scanf("%s", s + 1); m = strlen(t + 1); n = strlen(s + 1); scanf("%d", &k); for (int i = 1; i <= k; i++) scanf("%d %d", &a[i].l, &a[i].r), a[i].l++, a[i].r++; std::sort(a + 1, a + k + 1, cmp); a[k + 1] = (node){n + 1}; int st = a[1].l, ed = a[1].r; for (int i = 1; i < st; i++) b[++cnt] = (node){i, i, 1}; for (int i = 1; i <= k + 1; i++) if (a[i].l <= ed) ed = std::max(ed, a[i].r); else { b[++cnt] = (node){st, ed, ed - st + 1}, st = a[i].l, ed = a[i].r; for (int j = b[cnt].r + 1; j < a[i].l; j++) b[++cnt] = (node){j, j, 1}; } for (int i = 1; i <= n; i++) { pre[i][s[i] - 96]++; for (int j = 1; j <= 26; j++) pre[i][j] += pre[i - 1][j]; } int flag; for (int i = cnt; i >= 1; i--) for (int j = m; j >= 1; j--) { memset(tmp, 0, sizeof(tmp)); flag = 0; for (int l = j; l < j + b[i].len && l <= m; l++) { if (tmp[t[l] - 96] == pre[b[i].r][t[l] - 96] - pre[b[i].l - 1][t[l] - 96]) { f[i][j] = l - j; flag = 1; break; } tmp[t[l] - 96]++; } if (!flag && j + b[i].len <= m) f[i][j] = b[i].len + f[i + 1][j + b[i].len]; memset(tmp, 0, sizeof(tmp)); flag = 0; for (int l = j - 1; l > j - b[i - 1].len && l >= 1; l--) { if (tmp[t[l] - 96] == pre[b[i - 1].r][t[l] - 96] - pre[b[i - 1].l - 1][t[l] - 96]) break; tmp[t[l] - 96]++; flag++; } ans = std::max(f[i][j] + flag, ans); } printf("%d", ans); }