5. 最长回文子串(JAVA)LeetCode Hot 100

5. 最长回文子串

LeetCode题目地址

题目

给你一个字符串 s,找到 s 中最长的回文子串。

示例1

输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。

示例2

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

示例3

输入:s = “a”
输出:“a”

解法1(暴力)

思路
  • 列举所有的字串,哦按段是否是回文串,保存最长的回文串。
  • 利用两层for循环,时间复杂度为O(n²),for循环中,判断是否是回文串是O(n),所以时间复杂度为O(n³)
public String longestPalindrome(String s) {
        String ans = "";
        int max = 0;
        int len = s.length();
        for (int i = 0; i < len; i++)
            for (int j = i + 1; j <= len; j++) {
                String str = s.substring(i, j);
                // 如果str是回文串并且长度大于之前的最长的回文串长度,更新最长长度
                if (isPalindromic(str) && str.length() > max) {
                    ans = s.substring(i, j);
                    max = ans.length();
                }
            }
        return ans;
    }
	// 判断是否是回文串
    public boolean isPalindromic(String s) {
        int len = s.length();
        for (int i = 0; i < len / 2; i++) {
            if (s.charAt(i) != s.charAt(len - i - 1)) {
                return false;
            }
        }
        return true;
    }

解法2(动态规划)

思路
  • 确定dp数组以及下标的含义
  • 布尔类型的dp[i][j]:表示区间范围[i,j]的子串是否是回文子串,true表示是回文子串,false表示不是。
  • s.charAt(i) != s.charAt(j),dp[i][j] = false;
  • s.charAt(i) == s.charAt(j)
    情况1:i和j相同,同一个字符,是回文子串
    情况2:i和j相差为2,比如aba,也是回文子串
    情况3:i和j相差大于2的时候,例如abcba,此时s.charAt(i) == s.charAt(j),就看i到j区间是不是回文子串就看bcb是不是回文子串就可与i了,bcb的区间是i+1于j-1区间,这个区间是不是回文就看dp[i + 1][j -1] 是不是true。

时间复杂度O(n²)

public String longestPalindrome(String s) {
  	 	int n = s.length();
        if (n < 2) {
            return s;
        }
        int maxStart = 0;  //最长回文串的起点
        int maxEnd = 0;    //最长回文串的终点
        int maxLen = 1;  //最长回文串的长度
        boolean[][] dp = new boolean[n][n];
        for (int r = 1; r < n; r++) {
            for (int l = 0; l < r; l++) {
                if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
                    dp[l][r] = true;
                    if (r - l + 1 > maxLen) {
                        maxLen = r - l + 1;
                        maxStart = l;
                        maxEnd = r;
                    }
                }
            }
        }
        return s.substring(maxStart, maxEnd + 1);
    }

解法3(扩展中心)(参考该题解)

思路
  • 回文串一定是对称的,所以每次循环选择一个中心,进行左右扩展,判断左右字符是否相等即可。
  • 当字符串的长度是奇数时,从每一个字符串开始扩展,是偶数时,从两个字符之间开始扩展, 所以总共有n + n - 1个扩展中心。

时间复杂度O(n²)

public String longestPalindrome(String s) {
    int start = 0, end = 0;
    for (int i = 0; i < s.length(); i++) {
        int len1 = center(s, i, i);
        int len2 = center(s, i, i + 1);
        int len = Math.max(len1, len2);
        if (len > end - start) {
        	// 更新最长回文串的起点和终点
            start = i - (len - 1) / 2;
            end = i + len / 2;
        }
    }
    return s.substring(start, end + 1);
}

private int center(String s, int left, int right) {
    int Left = left, Right = right;
    while (Left >= 0 && Right < s.length() && s.charAt(Left) == s.charAt(Right)) {
    // 向左右同时扩散
        Left--;
        Right++;
    }
    return Right - Left - 1;
}

解法4(Manacher’s Algorithm)

思路
  • 有点没看懂,详情看官方题解。(官方题解)

时间复杂度O(n)

public String longestPalindrome(String s) {
        int start = 0, end = -1;
        StringBuffer t = new StringBuffer("#");
        for (int i = 0; i < s.length(); ++i) {
            t.append(s.charAt(i));
            t.append('#');
        }
        t.append('#');
        s = t.toString();

        List<Integer> arm_len = new ArrayList<Integer>();
        int right = -1, j = -1;
        for (int i = 0; i < s.length(); ++i) {
            int cur_arm_len;
            if (right >= i) {
                int i_sym = j * 2 - i;
                int min_arm_len = Math.min(arm_len.get(i_sym), right - i);
                cur_arm_len = expand(s, i - min_arm_len, i + min_arm_len);
            } else {
                cur_arm_len = expand(s, i, i);
            }
            arm_len.add(cur_arm_len);
            if (i + cur_arm_len > right) {
                j = i;
                right = i + cur_arm_len;
            }
            if (cur_arm_len * 2 + 1 > end - start) {
                start = i - cur_arm_len;
                end = i + cur_arm_len;
            }
        }

        StringBuffer ans = new StringBuffer();
        for (int i = start; i <= end; ++i) {
            if (s.charAt(i) != '#') {
                ans.append(s.charAt(i));
            }
        }
        return ans.toString();
    }
public int expand(String s, int left, int right) {
    while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
        --left;
        ++right;
    }
    return (right - left - 2) / 2;
}
    

你可能感兴趣的:(leetcode热题,leetcode,java,算法)