动态规划+中心拓展法 5. 最长回文子串.516. 最长回文子序列 647. 回文子串_js 给定一个字符串s ,问该字符串里有多少个长度大于1的子串都是回文_豌豆射手GCC的博客-程序员秘密

技术标签: 算法  leetcode  动态规划  

5. 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:

输入: “cbbd”
输出: “bb”

解题思路
子串问题,动态规划
1.最优子问题
dp[i][j],表示第i个字符到第j个字符是否是回文字符串
若dp[i+1][j-1]是回文字符串且s[i]==s[j],则dp[i][j]也为回文字符串
2.难点:边界问题
当s为单个字符时
初始条件:当s为三个字符时,前后相等即为回文字符串
大于三个字符时,用传递公式dp[i][j]=dp[i+1][j-1];

class Solution:
    def longestPalindrome(self, s: str) -> str:
        #动态规划,找到最优子问题;
        #dp[i][j],确定j,遍历i看ij是否为回文字符串;
        size=len(s)
        dp=[[False for _ in range(size)] for i in range(size)] #初始化列表
        start=0
        Lens=0        #初始化为0,不然会跳过单个字符的情况,输出至少两个字符
        if size==1:   #长度为一,特殊情况
            return s

        for j in range(1,size):         
            dp[j][j]=True         #初始化,单个为true
            for i in range(j):               
                if j-i<3 and s[i]==s[j]:      #字符串长度小于等于3时,且相等
                    dp[i][j]=True                  #为true
                elif s[i]==s[j]:                  #长度若不小于3,递推公式
                    dp[i][j]=dp[i+1][j-1]       #右上递推
                if dp[i][j] and j-i+1>Lens:    #找最大值
                    start=i                     #找起点
                    Lens=j-i                      #长度为终点-起点
        return s[start:start+Lens+1]            #输出,右闭,要+1

C++
注意点
先遍历数组,把每个数组开头的单个初始化为1,即回文串;
每个字符开头的2个字符,若相等,初始化为1,即回文串;

以i结尾的子串,遍历每一个j,若j<i-1且s[j]==s[i],则该dp[j][i]=1( j 开头, i 结尾);
若i和j隔了不只一个数且s[j]==s[i],则dp[j][i]=dp[j+1][i-1];

必须先初始化以i结尾的子串,再逐个遍历i之前的字符j
根据公式dp[i][j]=dp[i+1][j-1];
开头的字符——从后往前推;
结尾的字符——从前往后推;
所以结尾循环在外,开头循环在内,先要得到dp[每一个开头][i-1],才能得到,dp[j+1][i-1];

class Solution {
    
public:
    string longestPalindrome(string s) {
    
        //dp[i][j]  //s[i]~s[j]为回文
        int len=s.size();
        if(len<=1) return s;
        vector<bool> tmp(s.size(),0);
        vector<vector<bool>> dp(s.size(),tmp);
        
        int M=0;
        int start=0; //初始化为0,不然单个数无法输出
        int end=0;
        for(int i=0;i<len;i++)
        {
    
            for(int j=i;j>=0;j--)
            {
    
                if(s[j]==s[i])
                {
    
                    if(i<=j+2)
                        dp[j][i]=1;
                    else dp[j][i]=dp[j+1][i-1];
                }
                if(dp[j][i]&& M<i-j)
                    {
    
                        M=i-j;
                        start=j;
                        end=i;
                    }
            }
        }
        return s.substr(start,end-start+1);
    }
};

动态规划要注意递推方向

解法2:中心拓展法
每个点可以作为一个中心拓展;
每两个点的中间可作为一个中心拓展;
对每个可拓展点进行拓展,返回拓展后的回文子串长度;

拓展函数

int Expendfromcenter(string &s, int left,int right)
    {
    
        int n=s.size();
        while(left>=0&&right<n&&s[left]==s[right])
        {
    
            --left;
            ++right;
        }
        return right-left-1;//返回长度   需要-2
    }

主函数

string longestPalindrome(string s) {
    
        int len=s.size();
        int maxlen=0;
        int start=0;
        int wide=0;
        int tmp1;
        int tmp2;
        for(int i=0;i<len;i++)
        {
    
            tmp1=Expendfromcenter(s,i,i);
            tmp2=Expendfromcenter(s,i,i+1);
            if(tmp1>maxlen)
            {
       
                maxlen=tmp1;
                start=i-tmp1/2;
                wide=tmp1;
            }
            if(tmp2>maxlen)
            {
    
                maxlen=tmp2;
                start=i-(tmp2/2)+1;
                wide=tmp2;
            }
        }
        
        return s.substr(start,wide);
    }

完整代码

class Solution {
    
public:
    string longestPalindrome(string s) {
    
        int len=s.size();
        int maxlen=0;
        int start=0;
        int wide=0;
        int tmp1;
        int tmp2;
        for(int i=0;i<len;i++)
        {
    
            tmp1=Expendfromcenter(s,i,i);
            tmp2=Expendfromcenter(s,i,i+1);
            if(tmp1>maxlen)
            {
       
                maxlen=tmp1;
                start=i-tmp1/2;
                wide=tmp1;
            }
            if(tmp2>maxlen)
            {
    
                maxlen=tmp2;
                start=i-(tmp2/2)+1;
                wide=tmp2;
            }
        }
        
        return s.substr(start,wide);
    }

private:
    int Expendfromcenter(string &s, int left,int right)
    {
    
        int n=s.size();
        while(left>=0&&right<n&&s[left]==s[right])
        {
    
            --left;
            ++right;
        }
        return right-left-1;//返回长度   需要-2
    }
};

516. 最长回文子序列

给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。

示例 1:

输入:

"bbbab"
输出:

4

一个可能的最长回文子序列为 “bbbb”。

示例 2:

输入:

"cbbd"
输出:

2

一个可能的最长回文子序列为 “bb”。

回文子序列
回文子序列可以不连续,回文子串一定连续;

解法:动态规划
dp【i】【j】表示从i开始到j结束的子串里回文子序列的大小;

(1)初始化:dp【i】【i】为1;
(2)递推:从左往右推,从下往上推(坐下——>右上);
当s【i】==s【j】时,dp【i】【j】=dp【i+1】【j-1】+2;
当s【i】!=s【j】时,dp【i】【j】=max(dp【i+1】【j】,dp【i】【j-1】;
(3)得到结果为dp【0】【n-1】;

class Solution {
    
public:
    int longestPalindromeSubseq(string s) {
    
        //最长回文子序列——可以不连续
        int n=s.length();
        vector<int> tmp(n,0);
        vector<vector<int>> dp(n,tmp);
        for(int j=0;j<n;j++) dp[j][j]=1;

        for(int i=n-2;i>=0;i--)
            for(int j=i+1;j<n;j++)
            {
    
                if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2;
                else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
            }
        return dp[0][n-1];
    }
};

对’bbbab‘得到的dp如下:

1 2 3 3 4 
0 1 2 2 3 
0 0 1 1 3 
0 0 0 1 1 
0 0 0 0 1 

647. 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。

示例 1:

输入: "abc"
输出: 3
解释: 三个回文子串: "a", "b", "c".

示例 2:

输入: "aaa"
输出: 6
说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa".

注意:

输入的字符串长度不会超过1000。

动态规划
对每两个ij判断是否为回文串,是则总数+1;

class Solution {
    
public:
    int countSubstrings(string s) {
    
        int n=s.size();
        vector<vector<int>> dp(n,vector<int>(n,0));
        //判断ij是不是回文
        int cnt=0;
        for(int i=n-1;i>=0;i--){
    
            dp[i][i]=1;
            ++cnt;
            for(int j=i+1;j<n;j++)
            {
    
                if(s[j]==s[i])
                {
    
                    if(j<i+2) dp[i][j]=1;
                    else dp[i][j]=dp[i+1][j-1];
                    if(dp[i][j]) ++cnt;
                }
            }
        }
        return cnt;
        }
};

若要dp i和j 之间的回文串数量,不满足最优子问题,如下,无法统计正确答案;

class Solution {
    
public:
    int countSubstrings(string s) {
    
        int n=s.size();
        vector<vector<int>> dp(n,vector<int> (n,0));
        //单个字符为一个,初始化为1
        //s[i]==s[j] dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+1;
        //s[i]!=s[j] dp[i][j]=max(dp[i+1][j],dp[i][j-1])+1;
        for(int i=n-1;i>=0;i--){
    
            dp[i][i]=1;
            for(int j=i+1;j<n;j++)
            if(s[i]==s[j])
                dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+1/;
            else 
                dp[i][j]=max(dp[i+1][j],dp[i][j-1])+1;
        }
        for(int i=0;i<n;i++){
    
            for(int j=0;j<n;j++)
                cout<<dp[i][j]<<" ";
                cout<<endl;
            }
        return dp[0][n-1];
    }
};

s[i]==s[j]时无法判断该子串整体是否为回文子串。

中心拓展法

对每个字符和两个字符中的点进行左右拓展,能拓展几次说明有几个回文字符串;

class Solution {
    
public:
    int countSubstrings(string s) {
    
        int n=s.size();
        int left;
        int right;
        int res=0;
        for(int i=0;i<2*n-1;i++)
        {
    
            left=i/2;
            right=left+i%2;
            while(left>=0&&right<n&&s[left]==s[right])
            {
    
                --left;++right;
                ++res;
            }
        }
        return res;
        }
};
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/BLUEsang/article/details/105051278

智能推荐

Hbase实战教程之happybase_vlinz的博客-程序员秘密

本文基于实验室已经搭建好的Hadoop平台而写,使用Python调用happybase库。1.thrift 是facebook开发并开源的一个二进制通讯中间件,通过thrift,我们可以用Python来操作Hbase        首先开启Hadoop平台的HadoopMaster的thrift服务,用Xshell连接HadoopMaster,用root用户登录,如果想关闭终端之后,t...

mac上launchpad上顽固图标的删除办法_weixin_30699831的博客-程序员秘密

在launchpad界面:1,按住control+option+command,图标会抖动2,单击待删除图标,图标上出现问号3,松开三键4,再次按下三键,这时图标左上角出现叉叉,点击即可!转载于:https://www.cnblogs.com/imChay/p/5730176.html...

Linux用户空间与内核地址空间_kunkliu的博客-程序员秘密

转载地址:https://blog.csdn.net/maopig/article/details/77800124Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数据可能不在内存中。  Linux内核地址映射模型x86 CPU采用...

关于IAP升级任务开发中遇到的一些问题汇总_gd32e230升级_New农民工的博客-程序员秘密

【前言】最近正在开发一个具有升级功能的项目,使用的是GD32的E230芯片。这里只阐述个人认知水平的观点,本人很菜,所以酌情观看本文。 关于IAP升级我还是第一次做,无非就是将内存分两份,一份用于Bootloader,一份用于APP。芯片共64K Flash空间,在做升级前,已经使用了公司的V3协议,所以就顺便也套用了升级的框架,升级的协议是在V3协议的基础上,利用数据段进行了处理,相当于协议套协议,具体内容下面再说。 一、分配内存: 将工程只保留最...

python双目标定_Python 实现相机双目标定(stereo calibration)_weixin_39775127的博客-程序员秘密

Python做事情就是简洁, C++干一天的事情, Python一个小时就搞完了。 相机的双目标定, 用python做就大约100行代码:mport numpy as npimport cv2import globw=11h=8class StereoCalibration(object):def __init__(self, filepath):# termination criteriasel...

bzoj2844 albus就是要第一个出场_aklm45097的博客-程序员秘密

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2844【题解】考虑$n$个数组成的基,大小为$k$,那么每种方案都有$2^{n-k}$可以取到。观察样例也能发现这个结论。然后就是正常的线性基统计,最后乘一个$2^{n-k}$,加一即可。# include &lt;stdio.h&gt;# inclu...

随便推点

2021-02-04-SpringBoot后端实体类校验和全局异常处理_热爱Java的程序猿的博客-程序员秘密

SpringBoot后端实体类校验和全局异常处理实体类检验实体类Controller类全局异常处理实体类检验实体类首先在实体的属性上添加对应的校验规则,比如 @NotBlank和 @Email注解:import javax.validation.constraints.Email;import javax.validation.constraints.NotBlank;@TableName("m_user")public class User implements Serializabl

微信小程序购物车 数量加减功能_this.data.num_程序员林的博客-程序员秘密

现在就为大家介绍这个小组件,在小程序中,该如何去写下图为本项目的图:wxml:&lt;!-- 主容器 --&gt; &lt;view class="stepper"&gt; &lt;!-- 减号 --&gt; &lt;text class="{{minusStatus}}" bindtap="bindMinus"&gt;-&lt;/text&gt; &lt;!-- 数值 --&gt; &lt;input type="nu...

fortran使用MKL函数库计算大型稀疏矩阵的特征值与特征向量_fortran 矩阵特征值_chder_白南的博客-程序员秘密

program scsrev implicit none integer, parameter :: n = 3 !// depend on your equation integer :: i, j, mm, tmp, fileid, first, num real(kind=8) :...

maven高级案例_maven案例_晒太阳的黑宝的博客-程序员秘密

mavenmaven高级应用maven基础知识回顾功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入maven高级应用maven基础回顾maven传统的web工程做一个数据查询maven工程拆分与聚合的思想把第二阶段做好的

iOS 利用dSYM定位crash_iOS_Apple的博客-程序员秘密

What is dSYM ?xCode 的每一次编译都会生成一个dsym文件,在其内部存储了16进制函数地址的映射。在App实际执行的二进制文件中,是通过地址来调用方法,所以在App Crash 的时候,第三方工具会抓到函数崩溃调用栈。通过对应的dsym 文件就可以找到对应的崩溃地址。具体怎么使用,看集成哪家的SDK,去官方文档看怎么查看崩溃信息。How to find dSYM ?...