手撕算法—— 最長迴文子串

題目描述

最長迴文子串
給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲 1000。

示例 1:

輸入: “babad”
輸出: “bab”

注意: “aba” 也是一個有效答案。
示例 2:

輸入: “cbbd”
輸出: “bb”

解題思路

看到這道題的時候,第一眼能想到的方法就是暴力破解,枚舉每個子串,看它是不是迴文子串,並且記錄最長的迴文子串。它的時間複雜度爲O(N^3),空間複雜度爲O(N),這裏N指的是數組的長度。
時間複雜度爲O(N^3)的原因是使用兩層for循環找到所有的子串,然後再使用一層for循環判斷該子串是否爲迴文子串。這裏我們不討論它的實現。

接下來我們討論如何使用動態規劃解決這個問題。

動態規劃解題模板

動態規劃解題是有章可循的,我們可以將其分爲以下五步:
(1)確定dp數組元素的含義
(2)確定狀態轉移方程
(3)確定初始值
(4)根據初始值和狀態轉移方程確定dp數組元素的值
(5)考慮空間壓縮

我們直接結合代碼來分析:
第一步:確定dp數組元素的含義

  • 對於最長迴文子串我們可以定義數組dp[i][j]表示區間[i,j]是否爲迴文子串。

第二步:確定狀態轉移方程

  • 如果 s[i]!=s[j] ,將dp[i][j]置爲false;
  • 如果 s[i]==s[j],並且dp[i+1][j-1]==true,我們則將dp[i][j]置爲true;
  • 考慮特殊情況如 “aa”, “aba” ,當s[i]==s[j]的時候可以直接將其置爲true,因此可以得到 s[i]==s[j]&&j-i<3時 dp[i][j]=true;

第三步:確定初始值

  • 每一個元素都爲一個迴文子串,因此對於 0<i<s.length(),dp[i][i]=true;

第四步:根據初始值和狀態方程確定數組元素的值

  • 這裏使用兩層for循環將確定每個子串是否爲迴文。

第五步:考慮空間壓縮

代碼實現

class Solution {
    public String longestPalindrome(String s) {
        //判非
        if(s==null||s.length()<=1){
            return s;
        }
        
        int len=s.length();
        char[] arr=s.toCharArray();

		//確定數組元素含義
        boolean dp[][]=new boolean[len][len];

        //初始化
        for(int i=0;i<len;i++){
            dp[i][i]=true;
        }
     
        int maxLen=1;//記錄最大回文子串的長度
        int start=0;//記錄最大回文串的起始座標
		
		//根據初始值和狀態方程確定數組元素的值
        for(int j=1;j<len;j++){
            for(int i=0;i<j;i++){
                        if(arr[i]==arr[j]){
                            if(j-i<3||dp[i+1][j-1]){
                            dp[i][j]=true;
                            int l=j-i+1;
                            if(maxLen<l){//判斷長度
                                start=i;
                                maxLen=l;
                            } 
                        }
                    }else{
                        dp[i][j]=false;
                    }
            }
        }
        return s.substring(start,start+maxLen);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章