LeetCode-5.最长回文子串

题目

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

示例 1:

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

示例 2:

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

解题思路—中心扩展:遍历字符串,以每一个字符串都作为中间位,判断是否可以向左右扩展。这里要注意的是,i 可以作为偶数位回文中心,也可能作为奇数位回文中心。偶数位回文与奇数位回文的字符串中心是不一样的,所以要设置两个中心指针left和right如果是奇数位,left=right=i;如果是偶数位,left=i, right=i+1

解题思路—动态规划:设置一个数组boolean[][] dpdp[i][j]的定义为:字符串 i 到 j 是否为回文字符串如果dp[i][j]为回文字符串,则dp[i+1][j-1]肯定也是回文字符串。初始化时,dp[][]的对角线都设为true,因为单个字符本身也可以看作是回文字符串。
接下来,i 和 j 如何嵌套是关键问题!!!要保证判断 dp[i][j] 前 dp[i+1][j-1] 已经判断过了,所以 i+1 到 i ,i 采用升序遍历;j 到 j-1,j 采用降序遍历。同时要注意,i < j,这个条件一定要满足!

✨✨解题思路—Manacher算法:Manacher算法是一种解决最长回文子串问题的巧妙解法,就是过程不太容易理解,详细解释可以看这篇—Manacher算法解决最长回文子串问题,配合图解理解更快!这个方法非常推荐!!!!!

Java解题—中心扩展

class Solution {
    public String longestPalindrome(String s) {
        if(s==null || s.length()==0)
            return "";

        int start = 0, end = 0;
        for(int i=0;i<s.length();i++){
            int len1 = expandString(s, i, i); // 奇数
            int len2 = expandString(s, i, i+1); // 偶数
            int len = Math.max(len1, len2);
            if(len>end-start+1){
                start = i - (len-1)/2;
                end = start + len -1;
            }
        }
        return s.substring(start, end+1);
    }
    
    public static int expandString(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;
    }
}

Java解题—动态规划

class Solution {
    public String longestPalindrome(String s) {
        if(s==null || s.length()==0)
            return "";

        char[] ss = s.toCharArray();
        boolean[][] dp = new boolean[ss.length][ss.length];
        int left = 0, right = 0;
        for(int j=0;j<ss.length;j++){
            dp[j][j] = true; // s[i]本身就是回文字符串
            for(int i=j-1;i>=0;i--){
                if(ss[j]==ss[i] && (j-i<3 || dp[i+1][j-1])) // j-i<3 即字符串长度为3,在ss[j]==ss[i]成立的情况下,一定是回文字符串
                    dp[i][j] = true;
                if(dp[i][j] && right-left<j-i){
                    right = j;
                    left = i;
                }
            }
        }
        return s.substring(left, right+1);
    }
}

Java解题—Manacher算法

class Solution {
    public String longestPalindrome(String s) {
        if(s==null || s.length()==0)
            return "";
        
        int[] max = maxLcpsLength(s);
        StringBuilder str = new StringBuilder();
        while(max[1]-->0)
            str.append(s.charAt(max[0]++));
        return str.toString();
    }
    
    // 处理字符串
    public static char[] manacherString(String str){
        char[] chars = str.toCharArray();
        char[] res = new char[chars.length*2+1];
        int index = 0;
        for(int i=0;i<res.length;i++)
            // 偶数位加 # ,奇数位不变
            res[i] = (i&1)==0?'#':chars[index++];
        return res;
    }

    // 马拉车算法
    public static int[] maxLcpsLength(String str){
        char[] chars = manacherString(str);
        int[] len = new int[chars.length]; // 记录每个回文字符串的长度
        int right = -1; // 当前回文的右边界
        int cen = -1; // 当前回文的中心
        int[] max = {-1, -1}; // 记录回文的最大值
        for(int i=0;i<chars.length;i++){
            // 2*cen-i是i点关于cen的对称点
            // right>i时,Math.min(len[2*cen-i], right-i),两种情况取最小,往外扩
            // right<=i时,以i为中心的回文没有被访问过,所以当前回文字符串只有i,len[i]=1
            len[i] = right>i? Math.min(len[2*cen-i], right-i) : 1;
            // 以i为中心,左右扩
            while(i-len[i]>-1 && i+len[i]<chars.length){
                if(chars[i-len[i]]==chars[i+len[i]])
                    len[i]++; // 左右字符相等,符合回文,len++;
                else
                    break;
            }
            // 更新当前回文的右边界以及中心
            if(i+len[i]>right){
                right = i + len[i];
                cen = i;
            }
            if(max[1]<len[i]-1){
                max[0] = (i-1)/2 - (len[i]-2)/2;
                max[1] = len[i]-1;
            }
        }
        return max;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值