题目链接
题意
给定一个字符串\(S\),允许修改其中至多\(k\)个字符变为\(T\)。
记\(T\)的反转为\(T'\),求\(T\)与\(T'\)的最长公共子序列。
题解
结论
\(T\)与\(T'\)的最长公共子序列的长度 = \(T\)的最长回文子序列的长度
证明
part. 1
先证:\(T\)与\(T'\)的最长公共子序列的长度\(\leq T\)的最长回文子序列的长度
假设\(LCS\)的长度为\(k\),则存在\(i_1<\cdots <i_k\)和\(j_1>\cdots >j_k\)使得\(T_{i_1},T_{i_2},\cdots,T_{i_k}\)和\(T_{j_1},T_{j_2},\cdots,T_{j_k}\)是相同的。
则必\(\exists p,i_p<j_p\)且\(i_{p+1}\geq j_{p+1}\).(方便起见,记\(i_{k+1}=|T|+1,j_{k+1}=-1\).)
- 若\(i_{p+1}>j_{p+1}\),则序列\(T_{i_1},\cdots,T_{i_p},T_{j_p},\cdots,T_{j_1}\)和序列\(T_{j_k},\cdots,T_{j_{p+1}},T_{i_{p+1}},\cdots,T_{i_k}\)都是回文串,并且长度和为\(2k\). 因此,\(T\)含有一个长度至少为\(k\)的回文子序列;
- 若\(i_{p+1}=j_{p+1}\),则序列\(T_{i_1},\cdots,T_{i_p},T_{i_{p+1}},T_{j_p},\cdots,T_{j_1}\)和序列\(T_{j_k},\cdots,T_{j_{p+2}},T_{j_{p+1}},T_{i_{p+2}},\cdots,T_{i_k}\)都是回文串,并且长度和为\(2k\). 因此,\(T\)含有一个长度至少为\(k\)的回文子序列。
part. 2
再证:\(T\)与\(T'\)的最长公共子序列的长度\(\geq T\)的最长回文子序列的长度
假设\(T\)的最长回文子序列长为\(k\),则存在\(i_1<\cdots <i_k\)使\(T_{i_1},\cdots,T_{i_k}\)为回文子序列。
取\(j_1=n-i_k+1,\cdots,j_k=n-i_1+1\),则\(T'_{j_1}=T{i_k}=T{i_1},\cdots,T'{j_k}=T{i_1}=T{i_k}\),即\(T_{i_1},\cdots,T_{i_k}\)与\(T'_{j_1},\cdots,T'{j_k}\)相等,即为\(T\)与\(T'\)的公共子序列。
因此,\(T\)与\(T'\)的公共子序列长度至少为\(k\).
总结
综上,\(T\)与\(T'\)的\(LCS\)的长度 = \(T\)的最长回文子序列的长度。
dp
根据上面的结论,问题就转化成了:
对于给定的字符串\(S\),修改不超过\(k\)个字符,使得其最长回文子序列取到最长。
用\(dp[l][r][x]\)表示\(s[l..r]\)修改至多\(x\)个字符后的最长回文子序列的长度。
时间复杂度:\(O(N^3)\).
Code
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = (a); i < (b); ++i)
#define F2(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i > (b); --i)
#define dF2(i, a, b) for (int i = (a); i >= (b); --i)
#define maxn 310
using namespace std;
typedef long long LL;
char ss[maxn];
int dp[maxn][maxn][maxn];
int main() {
int k;
scanf("%s%d", ss, &k);
int n = strlen(ss), ans=0;
dF2(l, n-1, 0) {
F(r, l, n) {
F2(x, 0, k) {
if (ss[l]==ss[r]) dp[l][r][x] = dp[l+1][r-1][x]+(l==r?1:2);
else {
dp[l][r][x] = max(dp[l+1][r][x], dp[l][r-1][x]);
if (x) dp[l][r][x] = max(dp[l][r][x], dp[l+1][r-1][x-1]+2);
}
}
}
}
printf("%d\n", dp[0][n-1][k]);
return 0;
}