1. 程式人生 > >尋找最長回文字符串

尋找最長回文字符串

delete 最長回文 我們 字符 技術 必須 symmetric return 當當

首先講解一種簡單容易理解的暴力解法:復雜度為O(n^2)

解題思路是:第一,定義一個pStr指向字符串str,再定義一個p指向pStr,q指向pStr+1;

第二,找出一個字符*p與其下一個字符*q相同位置,比如oo,num++,index = p;然後比較這兩個相同字符*p,*q兩邊的字符是否相等,如果相等再向兩邊擴展p--,q++(p>str&&q!=‘\0‘)。如果p指向首部,即p=str,則調出while循環,再比較一次if(*p == *q),num++,index = q.

第三,如果發現連續兩個字符不相等,則讓pStr++,p=pStr,q =pStr+1.

第四,通過maxNum和maxIndex記錄下最長回文的數目和位置。

#include <iostream>
using namespace std;

void GetLongestSymmetricalLength(char* str)
{
    if(str==NULL)
        return;

    char* pStr = str;
    char* p = pStr;
    char* q = pStr+1; 
    int num=0;
    int maxNum=0;;
    char* index = pStr;
    char* maxIndex = index;

    while(*pStr != ‘\0‘)
    {
        while((*p != *q))
        {
            num = 0;
            pStr++;
            p = pStr;
            q = pStr+1;
        }
        while((*p == *q)&&(p > str)&&(*q != ‘\0‘))
        {
            num++;
            index = p;
            p--;
            q++;
        }
        if((p == str)&&(*p == *q)&&(*q != ‘\0‘))
        {
            num++;
            index = p;
        }

        if(num > maxNum)
        {
            maxNum = num;
            maxIndex = index;
        }

        pStr++;
        p = pStr;
        q = pStr+1;
    }
    cout << "Result: " ;
    for(int i=0;i<2*maxNum;++i)
        cout << *maxIndex++ << " ";
    cout << endl;
    cout << "maxNum: "  << maxNum << endl;
}

int main()
{
    char* str = "abbacaacab";
    GetLongestSymmetricalLength(str);

    return 0;
}

  

以下空間復雜度和時間復雜度均為O(n)

思路:

復雜度為O(n),即指遍歷一遍數組,因而必須創建一數組保存已走過字符的長度信息(之前每個字符的最長回文串)。

同時,如”aba“最長回文長度為3,中心為’b‘;”aa“最長回文長度為2,中心卻是兩字符中心,如此在統計長度時,需對奇偶串長分別比較,有什麽辦法使其統一嗎?——插入特殊字符,如‘#’,”aba“為”#a#b#a#“,”aa“為”#a#a#“,如此,奇偶情況的最長長度都分別有了對應的中心。具體如下:

1 、在各個字符間插入特殊字符‘#‘,將字符S轉換為T,如S = “abaaba", T = "#a#b#a#a#b#a#”。

2、定義數組P[length],其中P[i]表示以Ti為中心的最長回文長度的一半(因為T添加了字符’#‘,故其一般即為S中最長回文子串,也即Ti最長邊界到Ti的距離)

如:

T = # a # b # a # a # b # a #

P = 0 1 0 3 0 1 6 1 0 3 0 1 0

P中最長的長度為6,而6即為S的最長回文子串。顯然,當長度為偶數時,在T中對應的是’#‘;為奇數時,對應的是原有字符,這也就是為何要添加附加字符的原因——統一奇偶情況。

下面問題的關鍵即為如何得到P數組。

技術分享圖片

假設現在已經遍歷到i = 13的位置,原字符串S的最長回文長度在T中對應位置C = 11,對應原字符串S的長度為9,字符串為”abcbabcba“。C的兩邊界分別為L = 2,R = 20;i關於C對應的位置為i‘ = 9。現在如何確定P[i]的大小呢?

圖中,i‘下方的綠色實線表明了關於i‘為中心的左右邊界,又i‘邊界被包含在L、R之間,故i下方的綠色實線區域也一定是對稱的。(即i‘、i以C對稱,i‘為中心的長度邊緣小於邊界L、R,則P[i] = P[i‘]),令P[i] = p[i‘] = 1。

下面討論另一種情況(即i‘、i以C對稱,i‘為中心的長度邊緣超出邊界L或R),如下圖

技術分享圖片

假設現在已經遍歷到i = 15的位置,原字符串S的最長回文長度在T中對應位置C = 11,對應原字符串S的長度為9,字符串為”abcbabcba“。C的兩邊界分別為L = 2,R = 20;i關於C對應的位置為i‘ = 7。

圖中綠色實線表示i、i‘為中心在邊界L、R內一定對稱的區域;紅色實線表示超出邊界L、R可能無法保證i對稱的區域;綠色虛線表示橫跨C的區域。

顯然,我們只能判定綠色實線部分一定對稱,即P[i] >= 5,但余下部分是否匹配我們則需要一一比較。

綜上,得出以下結論:

if P[ i’ ] ≤ R – i,
then P[ i ] ← P[ i’ ]
else P[ i ] ≥ P[ i’ ]. (Which we have to expand past the right edge (R) to find P[ i ].

剩下一步更新C的位置則變得容易,當當前位置i的邊界超過原有R則需要更新,即

if P[i] + i > R

C = i;

R = i + P[i].

class Solution
{
	public:    //88 / 88 test cases passed.    //Runtime: 18 ms        
	string longestPalindrome(string s) 
	{       
		if (s.size() < 2) 
		{           
			return s;       
		}                //將字符串變為需要的形式       
		string pStr;   
		for (int i = 0; i < s.size(); ++i) 
		{         
			pStr.push_back(‘#‘);       
			pStr.push_back(s[i]);     
		}      
		pStr.push_back(‘#‘);                //尋找回文    
		int length = (int)pStr.size();    
		int *arrIndex = new int[length];//每個下標對應的回文長度,P    
		int mid = 0;//回文子串的中心位置,C    
		int mx = 0;//回文子串的邊界,R     
		int maxIndex = 0;      
		int maxLength = 0;            
		for (int i = 0; i < length; ++i)
		{       
			arrIndex[i] = 0;      
			int mirror = (mid << 1) - i;//i關於中心的對稱位置,i‘      
			if (mx > i) 
			{//是否超出邊界            
				arrIndex[i] = (arrIndex[mirror] < mx - i) ? arrIndex[i] : mx - i;  
			}      
			while ((i + arrIndex[i] + 1) < length && (i - arrIndex[i] - 1) >= 0 && pStr[i + arrIndex[i] + 1] == pStr[i - arrIndex[i] - 1]) 
			{            
				++arrIndex[i];     
			}       
			if (arrIndex[i] + i > mx) 
			{//如果邊緣超出了當前,需要更新         
				mid = i;         
				mx = i + arrIndex[i];     
			}       
			if (arrIndex[i] > maxLength) 
			{           
				maxLength = arrIndex[i];       
				maxIndex = (i - 1) >> 1;  
			}    
		}      
		delete[] arrIndex;         
		string outputStr(s, maxIndex - (maxLength - 1)/2, maxLength);    
		return outputStr;   
	}
};

  

尋找最長回文字符串