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. 拆分问题降级。

若字符串不是回文,那么只能在s0,n-2 和 s1,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\

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)

相关推荐
Jack2037 分钟前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树2 小时前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE21218 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE21219 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术1 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
怕浪猫1 天前
Electron 系列文章封面图
算法·架构·前端框架
徐小夕1 天前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法