求最长回文串

Posted x-huang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求最长回文串相关的知识,希望对你有一定的参考价值。

题目描述如下:

技术图片

 

能立刻想到的就是爆破,先试试无脑爆破,时间复杂度O(n^3),然后毫无意外的超时了,爆破代码如下:

技术图片
public String longestPalindrome(String s) {
        
        int max = 0;
        int length = s.length();
        // 字符串长度只有1个或为空,那么它自身就是回文串
        if (length <= 1) {
            return s;
        }
        
        String rtString = "";
        // 从第一个字符开始,统计以每个字符开头的能达到的最大回文子串
        for (int i = 0; i < length; i++) {
            for (int j = i + 1; j < length; j++) {
                
                String subStr = s.substring(i, j + 1);
                int left = 0;
                int right = subStr.length() - 1;
                boolean flag = true;
                // 判断是不是回文子串
                while (left <= right) {
                    if (subStr.charAt(left) == subStr.charAt(right)) {
                        left++;
                        right--;
                    }else {
                        flag = false;
                        break;
                    }
                }
                int size = subStr.length();
                // 进行最大长度记录
                if (flag && size > max) {
                    max = size;
                    rtString = subStr;
                }
            }
        }
        
        if (max == 0) {
            rtString = s.charAt(0) + "";
        }
        
        return rtString;
}
View Code

 

然后思考怎么使用动态规划,无奈本人动态规划渣渣,于是看题解

给出定义如下

技术图片

那么

P(i, j)=(P(i+1,j−1) and Si?==Sj?)

P(i,i)=true

P(i, i+1) = ( S_i == S_{i+1} )P(i,i+1)=(Si?==Si+1?)

 

恍然大悟,然后开始编码,动态规划算法代码如下:

技术图片
public String longestPalindrome1(String s) {
        
        if (s.length() <= 1) {
            return s;
        }
        
        int length = s.length();
        boolean[][] dp = new boolean[length][length];
        // 初始化单一字符为回文串
        for (int i = 0; i < dp.length; i++) {
            dp[i][i] = true;
        }
        
        // 因为dp[i][j]取决于d[i+1][j-1]的状态,所以自底向上进行动态规划
        for (int i = length - 1; i >= 0; i--) {
            for (int j = i; j < dp[i].length; j++) {
                if (j - i < 2) { //两个字符相邻或者就是同一个字符
                    dp[i][j] = (s.charAt(i) == s.charAt(j));
                }else { // 字符 i~j 是否为回文取决于 i+1 ~ j-1是否为回文 及 s[i] 是否等于 s[j]
                    dp[i][j] = (dp[i + 1][j - 1] && s.charAt(i) == s.charAt(j));
                }
            }
        }
        
        // 遍历dp数组找到最长回文串
        int maxlength = 0;
        int maxi = 0;
        int maxy = 0;
        for (int i = 0; i < dp.length; i++) {
            for (int j = i; j < dp.length; j++) {
                if (dp[i][j]) {
                    int len = j - i + 1;
                    if (len > maxlength) {
                        maxlength = len;
                        maxi = i;
                        maxy = j;
                    }
                }
            }
        }
        
        return s.substring(maxi, maxy + 1);
    }
View Code

 

继续看看还有什么算法,看到中心扩展法,即以某个字符为中心向两边扩展,时间复杂度为O(n^2)代码如下:

技术图片
public String longestPalindrome2(String s) {
        
        int length = s.length();
        if (length <= 1) {
            return s;
        }
        
        int max = 0;
        int startIndex = 0;
        // 这轮循环时计算
        for (int i = 0; i < length; i++) {
            
            int left = i;
            int right = i;
            
            // 求得回文串长度为奇数的串,返回值为{回文串串长度,回文串开始下标}
            int[] jiCuan = getCuan(s, left, right);
            // 求得回文串长度为偶数的串,返回值为{回文串串长度,回文串开始下标}
            int[] ouCuan = getCuan(s, left, right + 1);
            
            int bigger = 0;
            if (jiCuan[0] > ouCuan[0]) {
                bigger = jiCuan[0];
                left = jiCuan[1];
            }else {
                bigger = ouCuan[0];
                left = ouCuan[1];
            }
            
            if (bigger > max) {
                max = bigger;
                startIndex = left;
            }
        }
        return s.substring(startIndex, startIndex + max);
    }
    
    public int[] getCuan(String s, int left, int right) {
        // s[left](奇数回文串时)或s[left]~s[right](偶数回文串时)为中心向两边扩展
        while (left >= 0 && right < s.length()) {
            if (s.charAt(left) == s.charAt(right)) {
                left--;
                right++;
            }else {
                break;
            }
        }
        // 循环退出之前会把left-1,right+1,所以分别要加回来和减出去
        left = left + 1;
        right = right - 1;
        int[] a = {right - left + 1, left};
        return a;
    }
View Code

 

还有一种算法,比较难想到,时间复杂度为O(n),人称“马拉车算法”,这里给出参考链接:

https://www.cnblogs.com/grandyang/p/4475985.html

 

以上是关于求最长回文串的主要内容,如果未能解决你的问题,请参考以下文章

求最长回文子串,O(n)复杂度

数据结构与算法 Manacher 算法求最长回文子串

409.求最长回文串的长度 LongestPalindrome

UVa 11404 回文子序列(LCS求最长回文串长度)

[JSOI2016]扭动的回文串

求一个字符串中的最长回文串(Java)