Leetcode——5最長迴文子串

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

示例1:

輸入: "babad"
輸出: "bab"
注意: "aba" 也是一個有效答案。

示例2:

輸入: "cbbd"
輸出: "bb"

【思路】

解決這類 “最優子結構” 問題,可以考慮使用 “動態規劃”:

1、定義 “狀態”;
2、找到 “狀態轉移方程”。

記號說明: 下文中,使用記號 s[l, r] 表示原始字符串的一個子串,l、r 分別是區間的左右邊界的索引值,使用左閉、右閉區間表示左右邊界可以取到。舉個例子,當 s = ‘babad’ 時,s[0, 1] = ‘ba’ ,s[2, 4] = ‘bad’。

1、定義 “狀態”,這裏 “狀態”數組是二維數組。

dp[l][r] 表示子串 s[l, r](包括區間左右端點)是否構成迴文串,是一個二維布爾型數組。即如果子串 s[l, r] 是迴文串,那麼 dp[l][r] = true。

2、找到 “狀態轉移方程”。

首先,我們很清楚一個事實:

1、當子串只包含 11 個字符,它一定是迴文子串;

2、當子串包含 2 個以上字符的時候:如果 s[l, r] 是一個迴文串,例如 “abccba”,那麼這個迴文串兩邊各往裏面收縮一個字符(如果可以的話)的子串 s[l + 1, r - 1] 也一定是迴文串,即:如果 dp[l][r] == true 成立,一定有 dp[l + 1][r - 1] = true 成立。

根據這一點,我們可以知道,給出一個子串 s[l, r] ,如果 s[l] != s[r],那麼這個子串就一定不是迴文串。如果 s[l] == s[r] 成立,就接着判斷 s[l + 1] 與 s[r - 1],這很像中心擴散法的逆方法。

事實上,當 s[l] == s[r] 成立的時候,dp[l][r] 的值由 dp[l + 1][r - l] 決定,這一點也不難思考:當左右邊界字符串相等的時候,整個字符串是否是迴文就完全由“原字符串去掉左右邊界”的子串是否迴文決定。但是這裏還需要再多考慮一點點:“原字符串去掉左右邊界”的子串的邊界情況。

1、當原字符串的元素個數爲 3 個的時候,如果左右邊界相等,那麼去掉它們以後,只剩下 1 個字符,它一定是迴文串,故原字符串也一定是迴文串;

2、當原字符串的元素個數爲 2個的時候,如果左右邊界相等,那麼去掉它們以後,只剩下 0個字符,顯然原字符串也一定是迴文串。

把上面兩點歸納一下,只要 s[l + 1, r - 1] 至少包含兩個元素,就有必要繼續做判斷,否則直接根據左右邊界是否相等就能得到原字符串的迴文性。而“s[l + 1, r - 1] 至少包含兩個元素”等價於 l + 1 < r - 1,整理得 l - r < -2,或者 r - l > 2。

綜上,如果一個字符串的左右邊界相等,以下二者之一成立即可: 1、去掉左右邊界以後的字符串不構成區間,即“ s[l + 1, r - 1] 至少包含兩個元素”的反面,即 l - r >= -2,或者 r - l <= 2; 2、去掉左右邊界以後的字符串是迴文串,具體說,它的迴文性決定了原字符串的迴文性。

於是整理成“狀態轉移方程”:

dp[l, r] = (s[l] == s[r] and (l - r >= -2 or dp[l + 1, r - 1]))
或者

dp[l, r] = (s[l] == s[r] and (r - l <= 2 or dp[l + 1, r - 1]))
編碼實現細節:因爲要構成子串 l 一定小於等於 r ,我們只關心 “狀態”數組“上三角”的那部分取值。理解上面的“狀態轉移方程”中的 (r - l <= 2 or dp[l + 1, r - 1]) 這部分是關鍵,因爲 or 是短路運算,因此,如果收縮以後不構成區間,那麼就沒有必要看繼續 dp[l + 1, r - 1] 的取值。

參考鏈接:https://leetcode-cn.com/problems/two-sum/solution/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/

【實現代碼】

package leetcode_50;

import java.util.ArrayList;
import java.util.Scanner;

public class Leetcode_5_2 {

	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
		String s=sc.nextLine();
		String result;
		result=longestPalindrome(s);
		System.out.print(result);

	}

	private static String longestPalindrome(String s) {
		int len=s.length();
		if(len<=1){
			return s;
		}
		int longestPalindrome=1;
		String longstr=s.substring(0,1);
		boolean[][] dp=new boolean[len][len];
		for(int r=1;r<len;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>longestPalindrome){
						longestPalindrome=r-l+1;
						longstr=s.substring(l,r+1);
					}
				}
			}
		}
		return longstr;
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章