P8638 [蓝桥杯 2016 省 A] 密码脱落

发布时间 2023-12-22 17:01:55作者: Gold_stein

基本可以确定这道题是一个dp,我首先想到的思路是,根据回文序列对称的特性,把这个原序列分成前后两半来做,但是每次对序列进行添加操作,都会导致中心点的移动,导致这种做法非常麻烦,因此需要转换思路:

不妨直接把整个序列颠倒过来,那些本身是回文串的部分,颠倒之后还是回文串,而剩下的那些部分,为了把它们变成回文串,就需要在原序列的对称位置,添加相同字母,则答案就是

len(原)-len(最大回文子串)

而len(最大回文字串)=原序列和颠倒后序列的最长公共子序列长度,即LCS

代码如下:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#define For(i, j, n) for(int i = j ; i <= n ; ++i)
using namespace std;

const int N = 1005;

char str[N], ctr[N];
int f[N][N];

int main()
{
    cin >> str;
    int len = strlen(str);
    for(int i = 0; i < len; i++)
        ctr[len - 1 - i] = str[i];
    for(int i = 0; i < len; i++)
        for(int j = 0; j < len; j++)
        {
            if(str[i] == ctr[j])
            {
                if(i < 1 || j < 1)
                    f[i][j] = 1;
                else
                    f[i][j] = f[i - 1][j - 1] + 1;
            }
            else
            {
                int a = (i < 1) ? 0 : f[i - 1][j],
                    b = (j < 1) ? 0 : f[i][j - 1];
                f[i][j] = max(a, b);
            }
        }
    cout << len - f[len - 1][len - 1];
    return 0;
}