天天看點

Leetcode---Longest Palindromic Substring最長回文字串(Longest Palindromic Substring)

最長回文字串(Longest Palindromic Substring)

題目:Given a string S, find the longest palindromic substring in S. You may assume that the             maximum length of S is 1000, and there exists one unique longest palindromic substring.

題目連接配接:https://leetcode.com/problems/longest-palindromic-substring/

    大意是給定一個字元串S,求S中最長的回文字串,回文字元串是指從坐到右和從右到左都是一樣的字元串,例如:“西湖回遊魚遊回湖西”、“ABDCDBA”、“abccba”等等諸如此類的字元串,單個字元的串,也是回文串。

思路1:暴力解法,枚舉S的所有字串,判斷每個字元串是否是回文串,傳回最長的串,當字元串很長的時候,枚舉也隻能想想了,更别說還要判斷是否是回文。 思路2:枚舉中心法,我們感興趣的隻是串中的回文字串,因為回文串的性質,那麼我們可以枚舉串中每一個字元,來檢查以它為中心的串是不是回文串,并選取其中最長的傳回。            例如: ABDCDBA       1.以A為中心,A的左邊沒有字元,是以隻能是A當作一個回文。 2.以B為中心,那麼比較B的左邊和右邊字元是否相等。相等,是回文。否則,不是。 3.以此類推,直到所有的字元作為中心判斷完。 假設以i為中心,j為i左右的長度,且滿足條件1:(i-j)>=0 && (i+j)<S.size(); j從0開始,到不能滿足條件1結            束,分别判斷,判斷完成以i為中心後,以i+1為中心再進行類似判斷。當然還有一個問題,就是回文串的長度有          可能是奇數,也可能是偶數。例如ABA、ABBA都是回文。這個時候隻需要對奇數還是偶數進行分别判斷:                奇數時:判斷S[i-j] == S[i+j],j滿足(i-j)>=0 && (i+j)<S.size()。        偶數使:判斷S[i-j] == S[i+j+1],j滿足(i-j)>=0 && (i+j+1)<S.size() CODE:               

string LongestPalindrome( string s)
{
    int start=0,maxLen = 1,tlen = 1,ti=0;
    int size = s.size();
    
    if( size <= 1 )
        return s;
    
    
    // 奇數
    for( int i = 0; i < size; i++)
    {
        for( int j = 0; (i-j>=0) && (i+j
    
     = maxlen )
        {
            maxlen = tlen;
            start = i-j;
        }
        
        
          // 偶數
        for( int j = 0; (i-j>=0) && (i+j+1 <= n); ++j )
        {
            if( s[i-j] != s[i+j+1])
                break;
            tlen = 2*j+2;
            ti = i-j;
        }
        
        if(tlen > maxlen )
        {
            maxlen = tlen;
            start = ti;
        }
    }
    
    return s.substr(start,maxlen);


}
    
           

思路3:先看下面的例子:      A  AABABBAABCBABC ....       可以看出什麼? A按規定是一個回文,要判斷BAB是不是回文,那麼判斷B==B,且A是不是回文。判斷BAAB,則判斷B==B? 且AA是不是回文。以此類推,可以總結    到: 1.單個字元是回文。 2.兩個字元相同是回文,否則不是。 3.長度大于等于3的串,滿足s[0]==s[s.size-1] && s.substr(1,s.size-2)是不是回文。滿足則是,否則不是。    更一般化來講: 對于以i為起點,j為長度的string是不是回文串,隻需要判斷,s[i]==s[s+j-1]且s[i+1 to i+j-2]之間的串是不是回文串,這樣問題規模就減小了,對于規模    大的問題轉換到小規模問題求解,那麼很容易想到的就是動态規劃。     DP[i][i] = true;    i<s.size(); DP[i][i+1] = true;  if( s[i]==s[i+1] && i<s.size()-1) DP[i][j] = true;    if( s[i]==s[j] && DP[i+1][j-1] && i<size & j < size)    按照這個思路很自然可以得出代碼:    CODE    

string longestPalindrome(string s) {
   
        const int size = s.size();
        if( size == 1)
            return s;
            
        bool dp[1000][1000] ;
        
        for( int i = 0; i < 1000; i++)
            memset(dp[i],false,1000);
            
        int maxlen =1;
        int start = 0;
        
        for( int i = 0; i < 1000; i++)
        {
            dp[i][i]= true;
            if( i<(size-1) && (s[i] == s[i+1]))
            {
                dp[i][i+1]=true;
                start = i;
                maxlen = 2;
            }
        }
        
        for( int len = 3; len <= size; len++)
        {
            for(int i = 0; i < size-len+1; i++)
            {
                int j = i+len-1;
                if( dp[i+1][j-1] && (s.at(i)==s.at(j)))
                {
                    dp[i][j] = true;
                    start = i;
                    maxlen = len;
                }
            }
        }
        
        if( maxlen >= 2)
        {
            return s.substr(start,maxlen);
        }
        
        return NULL;
    }
           

  抛開暴力枚舉的方法,因為效率太低了,中心枚舉和動态規劃的時間複雜度都是O(n^2),還有沒有更高效的方法呢?當然有,這就是傳說中的Manacher算法,這個方法需要點想象力啊。

思路4:Manacher算法,這就是個傳說。 Manacher算法思想是來自上述“中心擴充法”,在“中心法”中要考慮回文 串是奇數還是偶數,有沒辦法統一處理呢?有,這就是Manacher算法。     該算法首先将字元串的每個字元之間加入一個特殊字元,并且在字元串開頭加入一個标志來更好處理越界和編碼問題。例如:s=“abba”編碼後是:s=“$#a#b#b#a#”。    然後,用一個數組p來記錄每個字元是s[i]為中心的的回文串向左或向右的長度,則原字元串s的回文長度為max(p[i]-1)。因為對于一個回文字元串s加入特殊字元後,該回文串加上特殊字元後依然會是一個回文串,且編碼後的回文串的長度為2*s.size+1。那麼數組記錄的最大值為s.size+1,比原回文的長度多1。故求取p[]的最大值減1就是原字元串中最大的回文串。    現在最重要的問題就是怎麼求解p[],這似乎又回到原來開始的問題了,非也。因為加了特殊字元,會出現一些有用的結論。    假設現在求p[i]的值,則p[0]...p[i-1]的值已經求出,maxlen為目前已經求出的回文串延伸到右邊的最大長度,id為p[0]到p[i-1]最大回文串的中心,j為i關于id的對稱點,即j=2*id-i。那麼:    1.如果i不在maxlen範圍内,即不在任何回文串中,則p[i]=1(本身為回文)。接着判斷p[i+j]==p[i-j],為真則++p[i],否則2;    2.p[i]>=min(p[2id-i],maxlen-i]。

   證明: 情況1:          

Leetcode---Longest Palindromic Substring最長回文字串(Longest Palindromic Substring)

情況2:

Leetcode---Longest Palindromic Substring最長回文字串(Longest Palindromic Substring)

情況3:

Leetcode---Longest Palindromic Substring最長回文字串(Longest Palindromic Substring)
int Manacher(string s)
{
	int len = s.size();
	int maxLen = 0;
	int id;
	int mx = 0;

	for (int i = 1; i < len; i++)
	{
		if (i < maxLen)
			p[i] = min(p[2 * id - i], maxLen - i);
		else
			p[i] = 1;

		while (s_new[i - p[i]] == s_new[i + p[i]])
			p[i]++;


		if (maxLen < i + p[i])
		{
			id = i;
			maxLen = i + p[i];
		}

		mx = max(mx, p[i] - 1);
	}

	return mx;
}