這道題應該是比較典型的利用kmp去dp的題目。
我們思考題意他要求我們的子序列不能夠和virus串完全匹配,那麼我們就可以在原先求最長公共子序列的基礎上在加一個維度。就是
f[i][j][z],第一個串枚舉到i,第二個串枚舉到j,然後他們與virus串匹配的長度是z。
接下來思考轉移。
第一個串當做A數組,第二個串當做B數組
即當(A[i] == B[j])的時候可以在原先已經有的子序列基礎上加上字符A[i],這個時候與virus匹配到的長度是z。那麼這個長度z是由誰轉移來的?
這個地方需要kmp去預處理。我們枚舉virus串的長度,然後就假設當前長度爲templen,然後枚舉下一個字符是誰‘A’-‘Z’,利用next數組就可以得到加上下一個字符後,與virus串匹配上的長度,(這個部分就是kmp的最基礎應用)
預處理後我們就可以知道有哪些狀態加上A【i】這個字符可以到達z狀態,這就可以進行轉移了。
唯一與最長公共子序列有區別就是(A[i] == B[j]),其他是一樣的。
複雜度就是O( | s1 | * | s2 | * | virus|)
在輸出路徑方面就是和大部分輸出路徑一樣倒推回去就好了
char A[max_], B[max_], S[max_];
int next_[max_],lenS,lenA,lenB;
il void getnext() {
lenS = strlen(S + 1);
int j = 0;
for (int i = 2; i <= lenS; i++) {
while (j&&S[j + 1] != S[i]){
j = next_[j];
}
if (S[j + 1] == S[i])j++;
next_[i] = j;
}
}
vector<int> to[max_][30];
int f[max_][max_][max_];
void dfs(int i, int j, int z) {
if (A[i] == B[j]) {
for (auto qian : to[z][A[i] - 'A']) {
if (f[i][j][z] == f[i - 1][j - 1][qian] + 1) {
dfs(i - 1, j - 1, qian);
cout << A[i];
return;
}
}
}
if (f[i][j][z] == f[i - 1][j][z]) {
dfs(i - 1, j, z); return;
}
if (f[i][j][z] == f[i][j - 1][z]) {
dfs(i, j - 1, z); return;
}
}
signed main() {
cin >> (A + 1) >> (B + 1) >> (S + 1);
lenA = strlen(A + 1);
lenB = strlen(B + 1);
getnext();
for (int i = 0; i <= lenS; i++) {
for (int j = 0; j <= 25; j++) {
int now = i;
while (now && S[now + 1]!='A'+j){
now = next_[now];
}
if (S[now + 1] == 'A' + j)now++;
to[now][j].push_back(i);
}
}
for (int i = 1; i <= lenA; i++) {
for (int j = 1; j <= lenB; j++) {
for (int z = 0; z < lenS; z++) {
if (A[i] == B[j]) {
for (auto qian : to[z][A[i] - 'A']) {
f[i][j][z] = max(f[i][j][z], f[i - 1][j - 1][qian] + 1);
}
}
f[i][j][z] = max(f[i][j][z], f[i - 1][j][z]);
f[i][j][z] = max(f[i][j][z], f[i][j - 1][z]);
}
}
}
int ans = 0,stu;
for (int i = 0; i < lenS; i++) {
if (ans < f[lenA][lenB][i]) {
ans = f[lenA][lenB][i];
stu = i;
}
}
if (ans == 0) { cout << 0; return 0; }
dfs(lenA, lenB, stu);
return 0;
}