最長迴文子串 -- 三種解答

秦懷雜貨店發表於2021-10-09

題目描述

給你一個字串 s,找到 s 中最長的迴文子串。

例子

示例 1:
輸入:s = "babad"
輸出:"bab"
解釋:"aba" 同樣是符合題意的答案。

示例 2:
輸入:s = "cbbd"
輸出:"bb"

示例 3:
輸入:s = "a"
輸出:"a"

示例 4:
輸入:s = "ac"
輸出:"a"

來源:力扣(LeetCode)
連結:https://leetcode-cn.com/probl...,著作權歸領釦網路所有。

思路以及解答

暴力破解

暴力破解,即是針對裡面每一個子串,都去判斷是否為迴文串。

判斷每一個字元是不是迴文串,比如用 cbac 判斷,左右兩個指標,對稱判斷,相等則往中間移動,繼續判斷,不相等則直接返回 false 。

    public static String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        String result = s.substring(0, 1);
        for (int i=0; i < s.length() - 1; i++) {
            for (int j = i + 1; j < s.length(); j++) {
                if (judge(s, i, j) && j - i + 1 > result.length()) {
                    result = s.substring(i, j+1);
                }
            }
        }
        return result;
    }
    
    // 判斷每個子串是不是迴文
    public static boolean judge(String source, int start, int end) {
        // 對稱軸對比
        while (start <= end) {
            if (source.charAt(start) != source.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

暴力破解複雜度過高,會超時,不推薦使用。

中心擴充法

迴文串總是中心對稱的,前面使用暴力法的時候,都是擷取出子串之後再判斷,只有判斷到全部對稱,才能證明迴文,這樣其實走了很多彎路,只要最後一個不對稱,前功盡棄。

反過來想,我們不如在每一個點,都嘗試往兩邊擴充,這樣只要不匹配,就可以及時止順。

值得注意的是,中心擴充法的中心怎麼找?3個字元有多少箇中心呢?

一共有五個中心,有些中心可能是兩個字元的間隙,有些中心可能是字元。那麼設計的時候,我們用 leftright 表示兩個指標:

  • left = right:對稱中心為字元
  • left + 1 = right: 對稱中心為兩個字元的間隙

具體實現如下:

class Solution {
    // 開始下標
    public static int start = -1;
    // 最大長度
    public static int maxLen= 0;
    public String longestPalindrome(String s) {
        start = -1;
        maxLen = 0;
        if(s==null||s.length()==0){
            return "";
        }
        for(int i=0;i<s.length();i++){
            // 以當前字元為對稱軸
            judge(s,i,i);
            // 以當前字元和下一個字元的間隙為對稱軸
            judge(s,i,i+1);
        }
        if(start == -1){
            return "";
        }
        return s.substring(start,start+maxLen);
    }

    public void judge(String s,int left,int right){
        while(left>=0 && right<s.length() && s.charAt(left)==s.charAt(right)){
            left--;
            right++;
        }
        int size =  right-left-1;
        if(size > maxLen){
            maxLen = size;
            start = left+1;
        }
    }
}

動態規劃

其實,一個字串是迴文串的話,那麼它倒過來讀也是一樣的,也就是說,它與它反轉後的字串,其實是完全匹配的,那麼要是我們用一個字串和它反轉字串一一統計匹配,是不是就可以得到結果呢?

答案是肯定的!假設原字串為 s1,反轉後的字串為 s2,字串長度為 n,我們用陣列 nums[n][n] 來記錄匹配的數量,nums[i][j]表示以 s1[i] 結尾的字元子串,和以 s2[j]結尾的字元子串,兩者的匹配字元的最大數值。

  • s1[i] == s2[j]:

    • 如果 i == 0 或者 j == 0: nums[i][j] = 1
    • 否則 nums[i][j] = nums[i - 1][j - 1] + 1;
  • 如果 s1[i] != s2[j],則 nums[i][j]=0

前面說的其實就是狀態轉移表示式,也就是 nums[i][j] 是怎麼求解的?nums[i][j] 是依賴於 nums[i - 1][j - 1] 和 當前字元是否匹配,如果當前字元不匹配,直接賦值為 0,只有在當前字元匹配的情況下,才會需要看前面一位的匹配數值 nums[i - 1][j - 1]

假設以 babad 為例子:

最後兩行的計算:

實現的程式碼如下:

class Solution {
    public static String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return "";
        }
        if (s.length() == 1) {
            return s;
        }
        int len = s.length();
        String s1 = new StringBuffer(s).reverse().toString();
        int[][] nums = new int[len][len];
        int end = 0, max = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (s1.charAt(i) == s.charAt(j)) {
                    if (i == 0 || j == 0) {
                        nums[i][j] = 1;
                    } else {
                        nums[i][j] = nums[i - 1][j - 1] + 1;
                    }
                }
                if (nums[i][j] > max) {
                    if (len - i - 1 + nums[i][j] - 1 == j) {
                        end = j;
                        max = nums[i][j];
                    }
                }
            }
        }
        return s.substring(end - max+1, end+1);
    }
}

作者簡介

【作者簡介】
秦懷,公眾號【秦懷雜貨店】作者,技術之路不在一時,山高水長,縱使緩慢,馳而不息。個人寫作方向:Java原始碼解析JDBCMybatisSpringredis分散式劍指OfferLeetCode等,認真寫好每一篇文章,不喜歡標題黨,不喜歡花裡胡哨,大多寫系列文章,不能保證我寫的都完全正確,但是我保證所寫的均經過實踐或者查詢資料。遺漏或者錯誤之處,還望指正。

劍指Offer全部題解PDF

2020年我寫了什麼?

開源程式設計筆記

相關文章