はまやんはまやんはまやん

hamayanhamayan's blog

Reversed LCS [AtCoder Grand Contest 021 D]

https://agc021.contest.atcoder.jp/tasks/agc021_d

前提知識

解法

https://agc021.contest.atcoder.jp/submissions/2134939

まず、問題を以下のように言い換える。
「文字列SからK文字を入れ替えて作れる部分列の回文の最大長は?」
これは区間DPをすることで計算できる。
f(l,r,k) := Sの[l,r]の区間でk文字以内の入れ替えで作れる部分列の回文の最大値
遷移は関数f内を見るといい。
メモ化再帰でO(N^3)
 
問題はどうやってこの言い換えを思いつくかであるが、自分は思いつかないのでネットを駆け回って探した証拠的な物を以下に乗せておく

string S; int K;
//---------------------------------------------------------------------------------------------------
int memo[303][303][303];
int f(int l, int r, int k) {
    if (memo[l][r][k]) return memo[l][r][k];
 
    int res = 0;
    if (l == r) res = 1;
    else if (l + 1 == r) {
        if (S[l] == S[r] or 0 < k) res = 2;
        else res = 1;
    } else {
        chmax(res, f(l + 1, r, k));
        chmax(res, f(l, r - 1, k));
        
        if (S[l] == S[r]) chmax(res, f(l + 1, r - 1, k) + 2);
        else if (0 < k) chmax(res, f(l + 1, r - 1, k - 1) + 2);
    }
 
    return memo[l][r][k] = res;
}
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> S >> K;
    int N = S.length();
 
    cout << f(0, N - 1, K) << endl;
}