AtCoder Grand Contest 021 D - Reversed LCS

题目链接

题意

给定一个字符串\(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\).)

  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\)的回文子序列;
  2. \(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;
}

posted @ 2018-02-25 22:20  救命怀  阅读(130)  评论(0编辑  收藏  举报