Leetcode 最长回文子串

目录

解法1:递归算法

解法2:Map取同字母位置法

解法3:中心扩展法

解法4:动态规划法

[解法5: Manacher算法](#解法5: Manacher算法)


示例 1:

复制代码
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

复制代码
输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

解法1:递归算法

1)判断输入的字符串是否是回文,若是肯定为最大回文串。或者字符串长度<=1 直接返回。(结束条件)

  1. 拆分问题降级。

若字符串不是回文,那么只能在s[0,n-2] 和 s[1,n-1]两个子串里去找最大回文串。

然后选取长度更大的那个返回结果即是结果。

方法很简单,代码如下:

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        if (isPalindrome(s)) {
            return s;
        }

        String left, right;
        int n = s.length();
        
        // find palindrome in left part
        left = longestPalindrome(s.substring(0, n-1));

        // find palindrome in right part
        right = longestPalindrome(s.substring(1, n));    

        if (left.length() > right.length()) {
            return left;
        } else {
            return right;
        }
        
    }

    public boolean isPalindrome(String s) {
        char[] schars = s.toCharArray();
        int n = schars.length;
        for (int i=0; i<n; i++) {
            if (schars[i] != schars[n-1-i]) {
                return false;
            }
        }
        return true;
    }
}

前面都能跑过,碰到测试用例:"babaddtattarrattatddetartrateedredividerb" 运行超时。

这个递归,很多重复计算,效率比较低。那就优化下,来个高效率点的吧。

解法2:Map取同字母位置法

我们知道每一个回文串,它的开头和结尾两个字母肯定相同。那么我们可以得到一个灵感,把所有字母的字符串中位置记录下来,回文只可能在那些出现了两次以上位置的字母里出现。

例如:"abcabcde"

Map [Character, List<Integer>]

a => [0, 3]

b => [1, 4]

c => [2, 5]

d => [6]

e => [7]

那么我们从字母的位移列表中,选出超过2个的进行检查所有的字符串是否为回文,然后取出最大的就可以了。

代码如下:

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        if (isPalindrome(s)) {
            return s;
        }

        // letter <-> count & index list
        Map<Character, List<Integer>> letterMap = new HashMap<>();
        char[] schars = s.toCharArray();

        for (int i=0; i<schars.length; i++) {
            Character c = Character.valueOf(schars[i]);
            List<Integer> indexs = letterMap.get(c);
            if (indexs != null) {
                indexs.add(Integer.valueOf(i));
            } else {
                indexs = new LinkedList<>();
                indexs.add(Integer.valueOf(i));
                letterMap.put(c, indexs);
            }
        }

        String maxRome = "" +schars[0];
        for (Map.Entry<Character,List<Integer>> entry: letterMap.entrySet()) {
            // more than one index position, maybe there is a palindrome
            List<Integer> indexList = entry.getValue();
            int indexSize = indexList.size();
            if (indexSize > 1) {
                for (int i=0; i<indexSize; i++) {
                    for (int j=indexSize-1; j>i; j--) {
                        int start = indexList.get(i);
                        int end = indexList.get(j) + 1;

                        // sub-string size is less than maxRome, skip it
                        if (end-start <= maxRome.length()) {
                            continue;
                        } else {
                            String subStr = s.substring(start,end);
                            if (isPalindrome(subStr) && subStr.length() > maxRome.length()) {
                                maxRome = subStr;
                            }
                        }
                    }
                }
            }
        }
        return maxRome;

       
        
    }

    public boolean isPalindrome(String s) {
        int n = s.length();
        for (int i=0; i<n; i++) {
            if (s.charAt(i) != s.charAt(n-1-i)) {
                return false;
            }
        }
        return true;
    }

}

运行效果:

虽然过了,但是5%的分布,效果不怎么样。稍微优化下。

1)代码中用的LinkedList来进行记录相同字母的位移下标,在频繁get的时候效率不如ArrayList高。

2)字母Map初始化的时候没有给定初始值,那么会导致resize占用时间。那么我们给定一个size*4/3作为它的初始值。

优化结果代码如下:

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        char[] schars = s.toCharArray();
        if (isPalindrome(schars, 0, s.length()-1)) {
            return s;
        }

        // letter <-> count & index list
        Map<Character, List<Integer>> letterMap = new HashMap<>(s.length()*4/3);
        for (int i=0; i<schars.length; i++) {
            Character c = Character.valueOf(schars[i]);
            List<Integer> indexs = letterMap.get(c);
            if (indexs != null) {
                indexs.add(Integer.valueOf(i));
            } else {
                indexs = new ArrayList<>();
                indexs.add(Integer.valueOf(i));
                letterMap.put(c, indexs);
            }
        }

        String maxRome = "" +schars[0];
        for (Map.Entry<Character,List<Integer>> entry: letterMap.entrySet()) {
            // more than one index position, maybe there is a palindrome
            List<Integer> indexList = entry.getValue();
            int indexSize = indexList.size();
            if (indexSize > 1) {
                for (int i=0; i<indexSize; i++) {
                    for (int j=indexSize-1; j>i; j--) {
                        int start = indexList.get(i);
                        int end = indexList.get(j);
                        int subStrLen = end-start+1;

                        // sub-string size is less than maxRome, skip it
                        if (subStrLen <= maxRome.length()) {
                            break;
                        } else {
                            String subStr = s.substring(start,end+1);
                            if (isPalindrome(schars, start, end)) {
                                maxRome = s.copyValueOf(schars, start, subStrLen);
                            }
                        }
                    }
                }
            }
        }
        return maxRome;

       
        
    }

    public boolean isPalindrome(char[] chars, int start, int end) {
        while(start < end) {
           if (chars[start++] != chars[end--]) {
                return false;
            }
        }
        return true;
    }

}

经过上面两个代码优化后,执行效果如下:

执行结果位于57%位置,还凑合吧。

解法3:中心扩展法

实现代码如下:

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        int length = s.length();
        if (length < 2) {
            return s;
        }

        char[] schars = s.toCharArray();
        String maxRome = "" +schars[0];

        // search max palindrome from the middle point schars[i]
        // palindrome middle from (0, len-1]
        for (int i=1; i<length; i++) {

            // find max even palindrome from middle point
            maxRome = search(schars, i-1, i, maxRome);

            // find max odd palindrome from middle point
            maxRome = search(schars, i-1, i+1, maxRome);
            
        }

        return maxRome;
    }

    public String search(char[] schars, int left, int right, String maxRome) {
        int length = schars.length;
        while(left >= 0 && right<length && schars[left] == schars[right]) {
            left--;
            right++;
        }

        if (right - left -1 > maxRome.length()) {
            return String.copyValueOf(schars, left + 1, right - left -1);
        }

        return maxRome;
    }

}

执行效果如下
93.2%分位,已经很好了。强烈推荐解法。

解法4:动态规划法

理解起来稍微复杂点,可以作为学习动态规划算法的例子。

实现代码如下:

java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        int length = s.length();
        if (length < 2) {
            return s;
        }

        // palindrome[i][j] = true => s[i,j] is palindrome
        boolean[][] palindrome = new boolean[length][length];

        // len = 1, all is palindrome
        for (int i=0; i<length; i++) {
            palindrome[i][i] = true;
        }

        int start = 0, maxLen = 1;
        char[] schars = s.toCharArray();
        // palindrome[i][j] = palindrome[i+1][j-1] && s[i]==s[j]
        for (int len = 2; len<=length; len++) {
            for (int i=0; i<length; i++) {
                // j - i + 1 = len
                int j = i + len -1;
                if (j>length-1) {
                    break;
                }

                if (schars[i] == schars[j]) {
                    if (len <= 3) {
                        palindrome[i][j] = true;
                    } else {
                        palindrome[i][j] = palindrome[i+1][j-1];
                    }
                } else {
                    palindrome[i][j] = false;
                }

                if (palindrome[i][j] && len > maxLen) {
                    start = i;
                    maxLen = len;
                }
            }
        }

        return String.copyValueOf(schars, start, maxLen);
    }
}

运行效果如下:

19.07%还凑合吧。

解法5: Manacher算法

还有个Manacher算法比较复杂,效果也没见得比"中心扩展法"好太多,就不推荐了。

感兴趣的童鞋可以去这里看
. - 力扣(LeetCode)

相关推荐
CoovallyAIHub3 小时前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
NAGNIP4 小时前
Serverless 架构下的大模型框架落地实践
算法·架构
moonlifesudo4 小时前
半开区间和开区间的两个二分模版
算法
moonlifesudo4 小时前
300:最长递增子序列
算法
CoovallyAIHub9 小时前
港大&字节重磅发布DanceGRPO:突破视觉生成RLHF瓶颈,多项任务性能提升超180%!
深度学习·算法·计算机视觉
CoovallyAIHub9 小时前
英伟达ViPE重磅发布!解决3D感知难题,SLAM+深度学习完美融合(附带数据集下载地址)
深度学习·算法·计算机视觉
聚客AI1 天前
🙋‍♀️Transformer训练与推理全流程:从输入处理到输出生成
人工智能·算法·llm
大怪v1 天前
前端:人工智能?我也会啊!来个花活,😎😎😎“自动驾驶”整起!
前端·javascript·算法
惯导马工1 天前
【论文导读】ORB-SLAM3:An Accurate Open-Source Library for Visual, Visual-Inertial and
深度学习·算法
骑自行车的码农1 天前
【React用到的一些算法】游标和栈
算法·react.js