P1435 [IOI2000] 回文字串 / [蓝桥杯 2016 省] 密码脱落


原题链接

P1435
题目类型: 普 及 + / 提 高 {\color{yellow} 普及+/提高} +/
AC记录:Accepted

题目大意

回文词是一种对称的字符串。任意给定一个字符串,通过插入若干字符,都可以变成回文词。此题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数。

输入格式

一个字符串。

输出格式

有且只有一个整数,即最少插入字符数

S a m p l e \mathbf{Sample} Sample I n p u t \mathbf{Input} Input

Ab3bd

S a m p l e \mathbf{Sample} Sample O u t p u t \mathbf{Output} Output

2

H i n t & E x p l a i n \mathbf{Hint\&Explain} Hint&Explain
字符串变成dAb3bAdAdb3bdA

数据范围

对于 100 % 100\% 100%的数据, 1 ≤ l e n g t h ≤ 1000 1≤length\le 1000 1length1000

解题思路

d p dp dp,就是 d p dp dp
其实这题看起来难,实际上很简单。
由于要计算回文的关系,首先设原来的字符串为 a a a,反转后的字符串为 b b b,即如果a="abc",则b="cba"。如果说,两个字符串里面都有同一个字符,那么这一个字符就不用被记入添加范围。否则,在原来的字符串中要加入一个同样的字符,所以,本题就变成了一个 求 最 长 公 共 子 序 列 \color{red}求最长公共子序列 的问题。


求最长公共子序列,无非就是把字符从两个字符串一个一个插入,然后进行动态转移。设 f i , j f_{i,j} fi,j为字符串 a a a中的前 i i i个字符与字符串 b b b中的前 j j j个字符的最长上升子序列的长度,考虑一下,如果两边插入的字符是一样的,那么就是从在没有插入这两个字符的情况中 + 1 +1 +1,即 f i , j = f i − 1 , j − 1 + 1 f_{i,j}=f_{i-1,j-1}+1 fi,j=fi1,j1+1。但如果不一样呢?那就要考虑前后顺序了。你可以先插入 a a a中,或者先插入 b b b中,即 f i , j = max ⁡ ( f i − 1 , j , f i , j − 1 ) f_{i,j}=\max(f_{i-1,j},f_{i,j-1}) fi,j=max(fi1,j,fi,j1)。整理,得:
f i , j = { f i − 1 , j − 1 + 1 a i = b j max ⁡ ( f i − 1 , j , f i , j − 1 ) a i ≠ b j f_{i,j}=\begin{cases} f_{i-1,j-1}+1 & a_i=b_j \\ \max(f_{i-1,j},f_{i,j-1}) & a_i\ne b_j \end{cases} fi,j={fi1,j1+1max(fi1,j,fi,j1)ai=bjai=bj
最后的答案就是 a . l e n g t h − f a . l e n g t h , a . l e n g t h a.length-f_{a.length,a.length} a.lengthfa.length,a.length

注意:

1.如果说 a a a的长度太大,例如 1 ≤ a . l e n g t h ≤ 5000 1\le a.length\le 5000 1a.length5000的话,平常的二维数组就不能使用了,要使用滚动数组
2.因为求出来的最长公共子序列是不被计算在答案里面的(不懂的再看一遍上面),所以在计算答案的时候要减去最长公共子序列的长度。


最后,祝大家早日
请添加图片描述

上代码

#include<iostream>
#include<cstring>

using namespace std;

string      a,b;
int         n;
int         f[3][5010];

void work(string x,string y)
{
    for(int i=1; i<=x.size(); i++)
    {
        for(int j=1; j<=y.size(); j++)
        {
            if(x[i-1]==y[j-1])
                f[2][j]=f[1][j-1]+1;
            else
                f[2][j]=max(f[1][j],f[2][j-1]);
        }
        for(int j=1; j<=y.size(); j++)
            f[1][j]=f[2][j];
    }
    return;
}

int main()
{
    // cout<<sizeof(f)/1024<<" KB"<<endl;
    cin>>a;
    n=a.size();
    b=a;
    for(int i=0,j=a.size()-1; i<j; i++,j--)
        swap(b[i],b[j]);
    work(a,b);
    cout<<n-f[1][n]<<endl;
    return 0;
}

完美切题 ∼ \sim

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值