leetcode_【5】最长回文子串

本文详细介绍了四种求解最长回文子串的方法:暴力破解、动态规划、中心扩展和Manacher算法,通过实例解释每种算法的原理和步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.题目描述

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

示例 1:

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

示例 2:

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

2.解题思路

方法1:暴力破解方法
方法2:动态规划

新建一个dp[i][j]代表的意思是索引从i到j的子字符串是否是回文,假设s = cbbd,则可以dp对应坐标索引下的子字符串:

i = 0123
j = 0ccbcbbcbbd
1bbbbbd
2bbd
3d

如果dp[i][j]所代表的字符串为回文,则将dp设为1.由上表可以总结出:

(1)当i = j 时,dp[j][i] = 1

(2)i-j=1时,比如dp[1][2]为bb,表示两个相邻的字符,此时我们只要判断str[1]==str[2]就能得出dp[1][2]的结果

(3)i-j>1时,我们来看dp[0][2],首先还是要判断开头和结尾是否相等,也就是判断 str[0]==str[2],假如此时str[0]=str[2],我们还要再看剩下的子串是否回文, 我们可以直接从dp[j+1][i-1]来判断剩下的子串,把结果直接拿来用,判断是否是1(1表示回文)

即有公式

     dp[i][j] = (dp[i+1][j-1] &&s[i]=S[j]​) == true?1:0

dp数组初始化如下:

     dp[i][i] = 1

    dp[i][i+1] = ( S[i] == S[i+1] ?1:0;
方法3:中心扩展方法

回文中心的两侧互为镜像。因此,回文可以从他的中心展开,并且只有2n-1个这样的中心(一个元素为中心的情况有n个,两个元素为中心的情况有n-1个)

方法4:manacher算法

(1)预处理:回文的长度可奇可偶,故在每个字符的左右都加上一个特殊字符“#”,得到长度为奇数的字符串

(2)回文子串的半径:以中间的 ‘1’ 为中心的回文子串 “#2#2#1#2#2#” 的半径是6,而未添加#号的回文子串为 “22122”,长度是5,为半径减1。

(3)子串的起始位置:(字符串前面在加一个特殊字符"$",在字符末尾加另一个特殊字符".")中间位置减去半径再除以2。

(4)子串的终点位置:起点位置+半径-1

(5)马拉车核心

   p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

其中p[i]代表以i为中心的半径,id为能延伸到最右端的位置的那个回文子串的中心点位置,mx是回文串能延伸到的最右端的位置

在这里插入图片描述

1)mx - i > p[j],即以j为半径的回文在以id为半径的回文内(j跟i是对称的),其中 j = 2*id - i,因为 j 到 id 之间到距离等于 id 到 i 之间到距离,为 i - id,所以 j = id - (i - id) = 2*id - i.
在这里插入图片描述

2)mx - i < p[j],即以j为中心的回文子串不一定完全包含于以id为中心的回文子串中,基于对称性可知,图中两个绿框所包围的部分是相同的,也就是说以i为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 p[i] = mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了,这就是后面紧跟到while循环的作用。

3)对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。

3.代码

思路1:暴力破解方法

 public static String longestPalindrome(String s) {
        String temp = "";
        String res  ="";
        for(int i = 0;i<s.length();i++){
            for(int j = i;j<s.length();j++){
                temp = temp+s.charAt(j);
                //re用来保存子字符串反转的结果
                String re  = new StringBuffer(temp).reverse().toString();
                //子字符串跟反转的字符串相等则为回文
                if(temp.equals(re)){
                    res = res.length()>temp.length()?res:temp;
                }
            }
            temp = "";
        }
        return res;
    }

方法2:动态规划思想

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if(s == null || len == 0){
            return s;
        }
        String res = "";
        int max = 0;
        //创建一个行列均为字符串长度的二维数组,创建时默认初始化为false
        boolean[][] dp = new boolean[len][len];
        for(int j = 0; j < len; j++){
            for(int i = 0; i <= j; i++){
                dp[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 2 || dp[i + 1][j - 1]);
                if(dp[i][j]){//如果是回文字符串
              //新得到的回文比之前的回文字符串要长,更新字符串长度,记录字符串
                    if(j - i + 1 > max){
                        max = j - i + 1;
                        res = s.substring(i, j + 1);
                    }
                }
            }
        }
        return res;
    }
}

方法3:中心扩展方法

class Solution {
   public  String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i); //奇数的回文,中心有一个字母,以该字母往外扩
            int len2 = expandAroundCenter(s, i, i + 1);  //偶数的回文,中心有两个字母,以这两个字母往外扩
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }
    private int expandAroundCenter(String s, int left, int right) {
        int L = left, R = right;
        while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
            L--;
            R++;
        }
        return R - L - 1;
    }
}

方法4:马拉车算法

class Solution { 
    public String  longestPalindrome(String s) {
         if (s == null || s.length() < 1) 
             return "";
 //manacher预处理
        //起点加特殊字符$ 
        String t = "$#";
        //每个字符左右都加特殊字符 #
        for (int i = 0; i < s.length(); ++i) {
            t += s.charAt(i);
            t += "#";
        }
        //终点加特殊字符&
        t+="&";
 //马拉车算法实现
        //定义半径
        int []p = new int[t.length()];
        int mx = 0, id = 0, resLen = 0, resCenter = 0;
        for (int i = 1; i < t.length()-1; i++) {
            //mancher核心算法
            p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
            //半径往外扩
            while (t.charAt(i + p[i]) == t.charAt(i-p[i]))
                ++p[i];
            //以i为中心的回文子串不一定完全包含于以id为中心的回文子串中
            if ((mx-i) <  p[i]) {
                mx = i + p[i];
                id = i;
            }
            if (resLen < p[i]) {
                resLen = p[i];
                resCenter = i;
            }
        }
        return s.substring((resCenter - resLen) / 2, (resCenter - resLen) / 2 + resLen - 1);
    }
}

4.提交记录

1.动态规划
动态规划
2.中心扩展
中心扩展
3.马拉车算法
马拉车算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值