5. Longest Palindromic Substring (M)

Longest Palindromic Substring (M)

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example 1:

Input: "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: "cbbd"
Output: "bb"

题意

给定一个字符串s,要求输出该字符串中的最大回文子串。

思路

  1. 暴力法。复杂度 O ( N 3 ) O(N^3) O(N3)
  2. 动态规划法。设若P[i][j]==true,则字符串s中从下标i到下标j构成的子串为回文子串,则很容易得到状态转移方程及边界条件如下,复杂度为 O ( N 2 ) O(N^2) O(N2)
    P [ i ] [ j ] = ( P [ i + 1 ] [ j − 1 ]   & &   s [ i ] = = s [ j ] ) P[i][j] = (P[i+1][j-1] \ \&\&\ s[i] == s[j]) P[i][j]=(P[i+1][j1] && s[i]==s[j])
    P [ i ] [ j ] = { t r u e , i = = j t r u e , j = = i + 1   & &   s [ i ] = = s [ j ] f a l s e , j = = i + 1   & &   s [ i ] ! = s [ j ] P[i][j] = \begin{cases} true, &i==j\\ true, &j==i+1 \ \&\&\ s[i]==s[j]\\ false, &j==i+1 \ \&\&\ s[i]!=s[j] \end{cases} P[i][j]=true,true,false,i==jj==i+1 && s[i]==s[j]j==i+1 && s[i]!=s[j]
  3. 中间扩展法。遍历字符串的每一个字符,奇数子串以该字符为中心向两边扩展搜索,直到两端字符不相同;偶数子串以该字符与下一个字符为中心向两遍扩展搜索,直到两端字符不相同。复杂度为 O ( N 2 ) O(N^2) O(N2)
  4. 马拉车算法 - Manacher’s Algorithm,复杂度为 O ( N ) O(N) O(N)

代码实现 - 动态规划

class Solution {
    public String longestPalindrome(String s) {
        // 特殊情况排除
        if (s == null || s.isEmpty()) {
            return "";
        }
        
        int maxLen = 0;
        int left = 0;
        int right = 0;
        boolean[][] P = new boolean[s.length()][s.length()];
        
        // 以子串长度及开始位置的变化进行循环
        for (int len = 1; len <= s.length(); len++) {
            for (int i = 0; i + len - 1 < s.length(); i++) {
                int j = i + len - 1;
                // 根据状态转移方程及边界条件生成数组P的值
                if (len == 1) {
                    P[i][j] = true;
                } else if (len == 2) {
                    P[i][j] = (s.charAt(i) == s.charAt(j));
                } else {
                    P[i][j] = (P[i + 1][j - 1] && s.charAt(i) == s.charAt(j));
                }
                if (P[i][j] && len > maxLen) {
                    maxLen = len;
                    left = i;
                    right = j;
                }
            }
        }
        return s.substring(left, right + 1);
    }
}

代码实现 - 中间扩展

class Solution {
    public String longestPalindrome(String s) {
        // 特殊情况处理
        if (s == null || s.isEmpty()) {
            return "";
        }
        
        int maxLen = 0;
        int left = 0;
        int right = 0;
        
        for (int i = 0; i < s.length(); i++) {
            // 奇数子串情况
            int len1 = fromCenter(i, i, s);
            // 偶数子串情况
            int len2 = fromCenter(i, i + 1, s);
            int len = Math.max(len1, len2);
            if (len > maxLen) {
                maxLen = len;
                left = i - (len - 1) / 2;
                right = i + len / 2;
            }
        }
        return s.substring(left, right + 1);
    }

    // 从目标字符向两边扩展,返回长度
    public int fromCenter(int i, int j, String s) {
        while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
            i--;
            j++;
        }
        return j - i - 1;
    }
}

代码实现 - Manacher

class Solution {
    public String longestPalindrome(String s) {
        // 特殊情况排除
        if (s == null || s.isEmpty()) {
            return "";
        }

        String t = transform(s);
        int[] p = new int[t.length()];
        // 中心位置
        int C = 0;
        // 右边界
        int R = 0;
        // 回文串最大半径
        int maxLen = 0;
        // 最长回文串对应中心
        int pos = 0;

        for (int i = 0; i < t.length(); i++) {
            // i关于C的对称点
            int j = 2 * C - i;
            
            // 分三种情况进行赋值
            p[i] = R >= i ? Math.min(p[j], R - i) : 0;
            
            // 回文半径可增加的情况
            while (i + p[i] + 1 < t.length() && i - p[i] - 1 >= 0 
            		&& t.charAt(i + p[i] + 1) == t.charAt(i - p[i] - 1)) {
                p[i]++;
            }

            if (p[i] > maxLen) {
                maxLen = p[i];
                pos = i;
            }
            
            // 更新中心点和右边界
            if (i + p[i] > R) {
                C = i;
                R = i + p[i];
            }
        }

        int left = (pos - maxLen) / 2;
        int right = (pos + maxLen - 1) / 2;
        return s.substring(left, right + 1);
    }

    // 字符串转换
    public String transform(String s) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            builder.append('#').append(s.charAt(i));
        }
        builder.append('#');
        return builder.toString();
    }
}

参考 - Manacher

博客园 - BIT祝威

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值