zoukankan      html  css  js  c++  java
  • 洛谷P1435 回文子串

    题目背景

    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所有题!

    给个赞再走呗?

    -------------------------------------------

    个性签名:学习使我快乐

    如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

    博主最近五行缺钱,请求精准扶贫

    赞助! 赞助! 赞助!

  • 相关阅读:
    如何自动生成参考文献格式
    VS2010+OpenCV 项目生成EXE文件如何在其他电脑上直接运行
    从多核CPU Cache一致性的应用到分布式系统一致性的概念迁移
    【译】为什么永远都不要使用MongoDB Why You Should Never Use MongoDB
    团队技能提升的二三事儿
    从微信朋友圈的评论可见性,谈因果一致性在分布式系统中的应用
    我所认为的软件可靠性的三重境界
    Redis核心原理与实践--事务实践与源码分析
    Redis核心原理与实践--Redis启动过程源码分析
    选择SaaS平台的那些事
  • 原文地址:https://www.cnblogs.com/laoguantongxiegogo/p/12272571.html
Copyright © 2011-2022 走看看