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)

相关推荐
zzzhpzhpzzz9 分钟前
设计模式——解释器模式
算法·设计模式·解释器模式
一只鸡某19 分钟前
实习冲刺第二十九天
数据结构·c++·算法·leetcode
鲨鱼吃橘子32 分钟前
【C++融会贯通】哈希表的使用
c语言·开发语言·数据结构·c++·链表·哈希算法·散列表
ZZZ_O^O38 分钟前
【贪心算法入门第一题——860.柠檬水找零】
学习·算法·leetcode·贪心算法
搬砖的小码农_Sky43 分钟前
C语言:共用体
c语言·开发语言·数据结构
Easy数模1 小时前
竞赛思享会 | 2024年第十届数维杯国际数学建模挑战赛D题【代码+演示】
python·算法·数学建模
向宇it2 小时前
【unity小技巧】Unity 四叉树算法实现空间分割、物体存储并进行查询和碰撞检测
开发语言·算法·游戏·unity·游戏引擎
无限大.2 小时前
冒泡排序(结合动画进行可视化分析)
算法·排序算法
nuo5342022 小时前
The 2024 ICPC Kunming Invitational Contest
c语言·数据结构·c++·算法