洛谷P1435 回文子串

摘要:
IOI2000的第一个主题描述回文是一个对称的字符串。例如,插入2个字符后,“Ab3bd”可以更改为回文“dAb3bAd”或“Adb3bdA”,但插入少于2个字符的字符不能更改为回语。原因将不予解释。直接的想法是:state:dp[i][j]从i到j的区间中最长的回文子序列的长度。

题目背景

IOI2000第一题

题目描述

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

比如 “Ab3bd”插入2个字符后可以变成回文词“dAb3bAd”或“Adb3bdA”,但是插入少于2个的字符无法变成回文词。

注:此问题区分大小写

输入格式

一个字符串(0<strlen<=1000)

输出格式

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

输入输出样例

输入:                                                     输出:
Ab3bd                                                    2

下面是这道题的题解:


这道题我一共有两种解法,下面我会把这两种解法都分享给大家:

第一种解法

第一种解法是用dp来解:

解题思路:这道题可以看做是一道求最长公共子序列的一道题(经典dp问题)!

为什么这么说呢,首先,回文串的特性就是正着读反着读都一样,一组对称的字符串。所以我们把这个字符串倒序放置也是和原来一样的。

仿佛就找到了一个突破口。

正序与倒序“公共”的部分就是我们回文的部分,如果把正序与倒序公共的部分减去你就会惊奇的发现剩余的字符就是你所要添加的字符,也就是所求的正解!

找到解题思路后我们就可以开始写了,最长公共自序列问题是个经典的dp问题,

最容易想到的方法就是开个二维数组dp[i][j],i,j分别代表两种状态;

那么我们的动态转移方程应该就是if(a[i]==b[j])   dp[i][j]=dp[i-1][j-1]+1;

依此即可解出最长公共自序列,用字符串长度减去即是正解

由于我比较懒,下面有不直接粘贴代码了。

如果你想要更优的解法,就可以把二维数组变一维,不过这里就不说了(a了就行了,哪里呢么多事)


第二种解法

一样是dp。。。。。。

不过这次利用的是区间dp。

区间dp就很好理解了。

不解释原因了,直接上思路:

状态:dp[i][j]从i到j区间内最长回文子序列长度。

转移方程:dp[i][j]=dp[i+1][j-1]+2 i和k能配对
           max(dp[i+1][j],dp[i][j-1]) i和k不能配对
状态:dp[i][i]=1
答案:dp[1][n]
复杂度:O(n^2)

下面是是dp部分代码:

for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=n-len+1;i++)
        {
            int j=i+len-1;
            if(a[i]==a[j])
                dp[i][j]=dp[i+1][j-1]+2;
            else
                dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
        }
    }

输入问题注意从a[1]开始输入

输出要用总长度-回文长度

下面是全部代码(区间dp)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define inf 100000000
//状态:dp[i][j]从i到j区间内最长回文子序列长度。
//转移方程:dp[i][j]=dp[i+1][j-1]+2        i和k能配对
//                   max(dp[i+1][j],dp[i][j-1])    i和k不能配对 
//状态:dp[i][i]=1
//答案:dp[1][n]
//复杂度:O(n^2)
char a[1001];
int dp[1001][1001];
using namespace std;
int main()
{
    scanf("%s",a+1);
    int n=strlen(a+1);
    for(int i=1;i<=n;i++)
    {
        dp[i][i]=1;
    }
    for(int len=2;len<=n;len++)
    {
        for(int i=1;i<=n-len+1;i++)
        {
            int j=i+len-1;
            if(a[i]==a[j])
                dp[i][j]=dp[i+1][j-1]+2;
            else
                dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
        }
    }
    cout<<n-dp[1][n];
    return 0;//不写return 0,考试就爆零
}

最后祝大家AC所有题!

给个赞再走呗?

免责声明:文章转载自《洛谷P1435 回文子串》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇图文详解一台电脑怎么设置两个显示器JS只能输入数字,数字和字母等的正则表达式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

windows下复制文件报错“文件名对目标文件夹可能过长 。您可以缩短文件名并重试,或者......”

我将一个路径下文件夹复制到另一个路径下时,出现了报错,报错图片如下: 然后查资料发现: 1、文件名长度最大为255个英文字符,其中包括文件扩展名在内。一个汉字相当于两个英文字符。2、文件的全路径名长度最大为260个英文字符,包含扩展名在内。如路径为C:Program Filesfilename.txt,那么这28个字符都包含在此字符数值中。一个汉字相当于...

[转]NSString/NSMutableString字符串处理和常用代码 (实例)

Objective-C 中核心处理字符串的类是 NSString 与 NSMutableString ,这两个类最大的区别就是NSString 创建赋值以后该字符串的内容与长度不能在动态的更改,除非重新给这个字符串赋值。而NSMutableString 创建赋值以后可以动态在该字符串上更改内容与长度。  NSString 常用方法总结 +(id)str...

getchar()和scanf()混合使用的坑

最近在混合使用 getchar() 和 scanf() 的时候遇到一个坑,现在记录一下。 代码中使用 getchar() 处理字符输入,用 scanf() 处理数字输入。 getchar() 读取每个字符,包括空格、制表符和换行符; 而 scanf() 在读取数字时则会跳过空格、 制表符和换行符。 比如下面这个程序,读入一个字符和两个数字,然后根据输入的两...

数据可视化之PowerQuery篇(五)PowerQuery文本处理技巧:移除和提取

https://zhuanlan.zhihu.com/p/64419762 每当拿到原始数据,不如意十有八九,快速准确的清洗数据也是必备技能,数据清洗正好是 PowerQuery 的强项,本文就来介绍两个常用的 M 函数:Text.Remove 和 Text.Select。 看到以 Text 开头的,就知道是文本处理函数,比如原始数据如下,     如果...

Java代码优化总结

  代码优化是一个很重要的课题。一般来说,代码优化的目标主要有两个,一个是减小代码的体积,另一个是提高代码运行的效率。   代码优化的细节有很多,此处列举部分:   1、尽量指定类、方法的final修饰符。   带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是fina...

Shell字符串

1.字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。 单引号字符串的限制: 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的; 单引号字串中不能出现单引号(对单引号使用转义符后也不行)。 双引号的优点: 双引号里可以有...