LeetCode 5.最長的迴文字符串

LeetCode 5.最長的迴文字符串

原文地址java

給定一個字符串s,找出其中最長的迴文格式的子字符串。你能夠假設長度的最大值爲1000.git

Example:github

Input: "babad"

Output: "bab"
複製代碼

Note: "aba" is also a valid answer.算法

Example:c#

Input: "cbbd"

Output: "bb"
複製代碼

一開始覺得palindrome是重複的意思,走了很大的彎路,後來才知道指的是迴文格式,就是一個順着讀和反過來讀都同樣的字符串。 由此可知他有兩種狀況,一種是奇數的狀況,中間的一個字符獨立,其他的字符以中間爲軸兩兩對應。另外一種是偶數的狀況,全部的字符都以中間爲軸兩兩對應。數組

方法一:

public class Solution {
       private int lo, maxLen;

       public String longestPalindrome(String s) {
           int len = s.length();
           if (len < 2)
               return s;

           for (int i = 0; i < len - 1; i++) {
               extendPalindrome(s, i, i);  //assume odd length, try to extend Palindrome as possible
               extendPalindrome(s, i, i + 1); //assume even length.
           }
           return s.substring(lo, lo + maxLen);
       }

       private void extendPalindrome(String s, int j, int k) {
           while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
               j--;
               k++;
           }
           if (maxLen < k - j - 1) {
               lo = j + 1;
               maxLen = k - j - 1;
           }
       }
   }
複製代碼

SuccessResult1.png

分析 這個方法分爲奇偶兩種狀況進行計算。首先對每個字符串裏的字符進行兩種狀況的計算,extendPalindrome方法的原理就是當對應的位置的字符相同時,就將左側字符向左一位,右側字符向右一位,而後再重複進行這個比較過程。 時間複雜度 : O(n^2) 。n是字符串的長度 空間複雜度 : O(n) .bash

原文地址微信

方法二:

public class Solution {
    public String longestPalindrome(String s) {
        String res = "";
        int currLength = 0;
        for(int i=0;i<s.length();i++){
            if(isPalindrome(s,i-currLength-1,i)){
                res = s.substring(i-currLength-1,i+1);
                currLength = currLength+2;
            }
            else if(isPalindrome(s,i-currLength,i)){
                res = s.substring(i-currLength,i+1);
                currLength = currLength+1;
            }
        }
        return res;
    }

    public boolean isPalindrome(String s, int begin, int end){
        if(begin<0) return false;
        while(begin<end){
        	if(s.charAt(begin++)!=s.charAt(end--)) return false;
        }
        return true;
    }
}

複製代碼

SuccessResult2.png

分析 這個方法主要是每當指針向右移時,咱們都以這個位置的字符爲結尾看是否能有新的長度爲length(current length +1 或者 current length +2)的迴文字符串。spa

時間複雜度 : O(n^2) 。n是字符串長度。 空間複雜度 : O(n) .3d

方法三 Manacher算法

public class Solution {
    public String longestPalindrome(String s) {
      char[] str = changeString(s);
		  String result = manacher(str,s);

		  return result;
}

/** * 返回例如 #a#c#b#c#a#a#c#b#c#d#形式的字符串數組 * * @param s * @return */
	public static char[] changeString(String s)
	{
	    char[] str = new char[s.length() * 2 + 1];

	    int i = 0;
	    for (; i < s.length(); i++)
	    {
	        str[2 * i] = '#';
	        str[2 * i + 1] = s.charAt(i);
	    }
	    str[2 * i] = '#';

	    return str;
	}

	/** * manacher 算法實現找到迴文字符串最長的一個 */
	public static String manacher(char[] s,String olds) {
		  String result = "";
	    int rad[] = new int[s.length];
	    int start = 0;
	    int end = 0;
	    //i index,j 迴文半徑,k
	    int i = 1, j = 0, k;

	    // 記錄最長的迴文串的長度
	    int maxLen = 0;
	    while (i < s.length)
	    {
	        // 掃描得出rad值
	        while (i - j - 1 > -1 && i + j + 1 < s.length
	                && s[i - j - 1] == s[i + j + 1]) {
	        	  j++;
	        }

	        if (maxLen < j ) {
				        maxLen =j;
				        start = i - j  ;
				        end =i + j ;

			       }
	        rad[i] = j;
	        maxLen = maxLen > j ? maxLen : j;

	        k = 1;
          //當迴文中包含子迴文,看子迴文是否超出父迴文邊界。 分三種狀況。
	        while (k <= rad[i] && rad[i - k] != rad[i] - k)
	        {
	            rad[i + k] = Math.min(rad[i - k], rad[i] - k);
	            k++;
	        }
	        i = i + k;
	        j = Math.max(j - k, 0);
	    }

	    result = olds.substring(start/2,end/2);
	    return result;

	}

}

複製代碼

SuccessResult3.png

分析 在這個問題中,迴文的狀況一共有兩種,一種是奇數迴文,一種是偶數迴文,爲了將他們合併成一種狀況,咱們能夠在首尾和每兩個字符中間加上一個特殊字符,如‘#’,形如"#a#b#b#c#a#".這樣咱們就將全部的迴文狀況合併成奇數迴文的狀況。

在字符串s中,咱們用rad[i]表示第i個字符的迴文半徑,能夠獲得s[i-rad[i],i-1] = s[i+1,i+rad[i]],只要求出了全部的rad,就求出了全部奇數長度的迴文子串。

當咱們獲得了rad[1..i-1]的值,並經過比較對稱字符獲得當前字符i的rad值至少爲j,求出了rad[i]。如今咱們設一個指針k,從1循環到rad[i],以此來求出[i+1,i+rad[i]]的rad值。

根據定義,黑色的部分是一個迴文子串,兩段紅色的區間全等。 由於以前已經求出了rad[i-k],因此直接用它.有3種狀況: (1)rad[i]-k < rad[i-k]

Situation1.jpg

如圖,rad[i-k]的範圍爲墨綠色。由於黑色的部分是迴文的,且墨綠色的部分超過了黑色的部分,因此rad[i+k]至少爲rad[i]-k,即橙色的部分。有沒有可能rad[i+k]的值大於rad[i]-k呢?這是不可能發生的,根據迴文的特性咱們知道,若是橙色部分之外也是迴文,那麼黑色的迴文部分就能夠向外拓展。與假設衝突。所以rad[i+k] = rad[i]-k。

(2)rad[i]-k > rad[i-k]

Situation2.jpg

如圖,rad[i-k]的範圍爲墨綠色。由於黑色的部分是迴文的,且墨綠色的部分在黑色的部分裏面,根據迴文特性,很容易得出:rad[i+k] = rad[i-k]。

根據上面兩種狀況,能夠得出結論:當rad[i]-k != rad[i-k]的時候,rad[i+k] = min(rad[i]-k,rad[i-k])。

(3)rad[i]-k = rad[i-k]

Situation3.jpg

如圖,經過和第一種狀況對比以後會發現,由於墨綠色的部分沒有超出黑色的部分,因此即便橙色的部分全等,也沒法像第一種狀況同樣引出矛盾,所以橙色的部分是有可能全等的。可是,根據已知的信息,咱們不知道橙色的部分是多長,所以就把i指針移到i+k的位置,j=rad[i-k](由於它的rad值至少爲rad[i-k]),是否能夠向外拓展等下個循環再作。

時間複雜度 : O(n) 。看起來程序裏用到了循環嵌套,可是實際上他只對沒有計量過的i進行計量。 空間複雜度 : O(n) .

若是你有更好的辦法或者對我這裏的描述有其餘見解,請聯繫我。謝謝 原文地址

About Me

個人博客 leonchen1024.com

個人 GitHub github.com/LeonChen102…

微信公衆號

CooderQRcodes.jpg
相關文章
相關標籤/搜索