AtCoder Grand Contest 021 D 题目链接 题意 题解 Code
题意
给定一个字符串(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
", dp[0][n-1][k]);
return 0;
}