LeeCode刷题笔记-5. 最长回文子串

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

示例 1: 输入: “babad” 输出: “bab”

注意: “aba” 也是一个有效答案。

示例 2: 输入: “cbbd” 输出: “bb”

题解:
中心扩展:

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 lenji=expandAroundCenter(s,i,i);//串长为奇数时
            int lenou=expandAroundCenter(s,i,i+1);//串长为偶数时
            int len=Math.max(lenji,lenou);
            if(len>end-start){//如果比当前最长还长 则更新
                start=i-(len - 1) / 2;
                end = i + len / 2;
            }
        }
        return  s.substring(start,end+1);
    }
    int expandAroundCenter(String s,int a,int b){
        while(a>=0&&b<s.length()&&s.charAt(a)==s.charAt(b)){
            a--;
            b++;
        }
        return b-a-1;
    } 
}

动态规划:

public class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        if (len < 2) {
            return s;
        }
        boolean[][] dp = new boolean[len][len];
        // 首先将数组对角线初始化为true,是动态规划的起点
        for (int i = 0; i < len; i++) {
            dp[i][i] = true;
        }
        int maxLen = 1;//maxlen和start用来存储当前求得的最大回文子串
        int start = 0;
        for (int j = 1; j < len; j++) {
            for (int i = 0; i < j; i++) {//由于子串起点i小于终点j,所以只需要处理一半的数组
                if (s.charAt(i) == s.charAt(j)) {//边界元素相同
                    if (j - i < 3) {//若串长很小可以直接判断
                        dp[i][j] = true;
                    } else {//动态规划
                        dp[i][j] = dp[i + 1][j - 1];//此时dp[*][j-1]一定已经求出
                    }
                } else {
                    dp[i][j] = false;
                }
                // 只要 dp[i][j] == true 成立,就表示子串 s[i, j] 是回文,此时记录回文长度和起始位置
                if (dp[i][j]) {
                    int curLen = j - i + 1;
                    if (curLen > maxLen) {
                        maxLen = curLen;
                        start = i;
                    }
                }
            }
        }
        return s.substring(start, start + maxLen);
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/longest-palindromic-substring/solution/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/

思路:
这道题一开始我只想到了暴力解,遍历所有的子串然后判断是否为回文子串。但是时间复杂度太高了,羞于拿出来示人…
而后参考了官方题解的写法(其实就是照搬哈哈哈哈),发现还有其他很多种神奇思路,分享如下:

  1. 最长公共子串法:把字符串S完全反转记为S’,然后S与S`的最长公共子串就是S的最长回文串。
    这个方法有个BUG,当 SS 的其他部分中存在非回文子串的反向副本时,最长公共子串法就会失败。为了纠正这一点,每当我们找到最长的公共子串的候选项时,都需要检查子串的索引是否与反向子串的原始索引相同。如果相同,那么我们尝试更新目前为止找到的最长回文子串;如果不是,我们就跳过这个候选项并继续寻找下一个候选。

  2. 动态规划:用一个Boolean型二维数组把不同起止的子串是否为回文子串存起来。dp[i][j] 表示子串 s[i, j] 是否为回文子串。因为一个字符串是否为回文子串可以分解为最左端和最右端的字符是否相等和去掉左右两端的元素之后的子串是否为回文串,即dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]。边界条件是:表达式 [i + 1, j - 1] 不构成区间,即长度严格小于 2,即 j - 1 - (i + 1) + 1 < 2 ,整理得 j - i < 3。(我也有点晕了,具体看上面的代码吧)

  3. 中心扩展算法:我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,由此我们遍历字符串,分别以字符串的每个点为中心寻找最长回文串。注意回文串长度奇偶数时情况不同 需要分开考虑。
    最后附上思路来源:
    官方题解
    大神题解

发布了10 篇原创文章 · 获赞 3 · 访问量 2266

猜你喜欢

转载自blog.csdn.net/qq_40397223/article/details/104713357