1. 程式人生 > 實用技巧 >Leetcode之最長迴文子串

Leetcode之最長迴文子串

問題描述

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

示例 1:

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

示例 2:

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

解法

思路很簡單,首先想到若情況為迴文字串奇數個字元時,中間的字元為軸。比較兩邊的字元是否相同。當迴文串字元為偶數時,剛開始假想它以空氣為軸,則找不到其他字元的下標。於是想到在每個字元包括兩邊都插入一個特殊字元比如(),這樣構成的迴文字串一定是奇數長度。也就是一定存在中間的某個字元,使得兩邊以它(可能是特殊字元也可能原字串中的字元)為軸,並且此字串的兩邊都以

結尾。

public static void longestPalindrome(String s) {
		//在每兩個字元中間插入特殊字元,保證迴文字串一定是奇數長度
		String str=s.replace("", "*");
		int maxl=1;
		int index=0;
		//從左到右遍歷,尋找以每一個字元為軸的最大長度
		for(int i=1;i<str.length();i++) {
			if(maxl<=2*Math.min(i,str.length()-i-1)+1) {//兩邊的長度可以超過最大長度
				for(int j=1;;j++) {
					if(j<=i&&j<=str.length()-i-1) {//兩邊的長度可以取到的值
						if(str.charAt(i-j)!=str.charAt(i+j)) {//這一個字元不是迴文串中的
							int l=2*j-1;
							if(maxl<l) {
								maxl=l;
								index=i;
							}
							break;
						}
					}else {//到頭了都是迴文串
						int l=2*j-1;
						if(maxl<l) {
							maxl=l;
							index=i;
						}
						break;
					}
				}
			}
		}
		//得到字串
		StringBuilder sb=new StringBuilder();
		for(int i=index+1-(maxl-1)/2;i<=index-1+(maxl-1)/2;i++) {
			if(i%2!=0) {
				sb.append(str.charAt(i));
			}
		}
    }

結果

官方解法

方法一 動態規劃

使用P[i][j]表示Si到Sj是否是一個迴文串。
動態規劃轉移方程可以寫為P[i][j]=P[i+1][j-1]&Si,即先看中間再加上首尾兩端。
邊界條件:因為單個的字元為迴文串,P[i][i]=true,兩個相鄰的相同字元為迴文串if Si==Si+1則P[i][i+1]=true
根據這個思路我自己寫了一個程式碼。跑出來的效果很差。

public static String longestPalindrome(String s) {
		if(s==null||s.isEmpty())
			return "";
		boolean[][]P=new boolean[s.length()][s.length()];
		//邊界
		int length=1;
		int start=0,end=0;
		for(int i=0;i<s.length()-1;i++) {
			if(s.charAt(i)==s.charAt(i+1)) {
				P[i][i+1]=true;
				if(length<2) {
					length=2;
					start=i;
					end=i+1;
				}
			}
			P[i][i]=true;
		}
		P[s.length()-1][s.length()-1]=true;
		//
		for(int i=0;i<s.length()-2;i++) {
			for(int j=0;j<s.length()-i-2;j++) {
				if(P[j+1][j+i+1]&&s.charAt(j)==s.charAt(j+i+2)) {
					P[j][i+j+2]=true;
					int l=i+3;
					if(length<l) {
						length=l;
						start=j;
						end=i+j+2;
					}
				}
			}
		}
		s=s.substring(start, end+1);
		return s;
    }

方法二 中心擴充套件

方法二的本質即為:我們列舉所有的「迴文中心」並嘗試「擴充套件」,直到無法擴充套件為止,此時的迴文串長度即為此「迴文中心」下的最長迴文串長度。這個演算法思想與我的很相似。但是跑出來的效果比我好。

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        int L = left, R = right;
        while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
            L--;
            R++;
        }
        return R - L - 1;
    }
}

方法三 Manacher演算法