最長回文字串

2021-09-09 00:25:34 字數 3498 閱讀 4224

回文串就是乙個正讀和反讀都一樣的字串,比如「level」或者「noon」等等就是回文串。回文子串,顧名思義,即字串中滿足回文性質的子串。比如輸入字串 "google」,由於該字串裡最長的對稱子字串是 "goog」,因此輸出4。

1.問題解決的基本方法

分析:可能很多人都寫過判斷乙個字串是不是對稱的函式,這個題目可以看成是該函式的加強版。

要判斷乙個字串是不是對稱的,不是一件很難的事情。我們可以先得到字串首尾兩個字元,判斷是不是相等。如果不相等,那該字串肯定不是對稱的。否則我們接著判斷裡面的兩個字元是不是相等,以此類推。

/*

*說明:求最長回文字串

*日期:2013-10-15

*/#include

using

namespace

std;

//字串是否對稱

bool isaym(char *cbegin, char *cend)

while(cbegin

cbegin++;

cend--;

}return

true

;}

現在我們試著來得到對稱子字串的最大長度。最直觀的做法就是得到輸入字串的所有子字串,並逐個判斷是不是對稱的。如果乙個子字串是對稱的,我們就得到它的長度,最後經過比較,就能得到最長的對稱子字串的長度了。

//

o(n*n*n)複雜度的子字串

int getmaxsym(char *str)

}plast --;

}pfirst ++;

}return

maxlength;

}

上述方法的時間效率:由於需要兩重while迴圈,每重迴圈需要o(n)的時間。另外,我們在迴圈中呼叫了issym,每次呼叫也需要o(n)的時間。因此整個函式的時間效率是o(n^3)。

假設輸入:abcddcba,按照上述程式,要分割成 'abcddcba』, 'bcddcb』, 'cddc』, 'dd』…等字串,並對這些字串分別進行判斷。不難發現,很多短子字串在長些的子字串中比較過,這導致了大量的冗餘判斷,根本原因是:對字串對稱的判斷是由外向裡進行的。

換一種思路,從裡向外來判斷。也就是先判斷子字串(如dd)是不是對稱的。如果它(dd)不是對稱的,那麼向該子字串兩端各延長乙個字元得到的字串肯定不是對稱的。如果它(dd)對稱,那麼只需要判斷它(dd)兩端延長的乙個字元是不是相等的,如果相等,則延長後的字串是對稱的。

2.改進的解決方案

根據從裡向外比較的思路寫出如下**:

//

改進後的程式

int getmaxsym2(char *str)

if(oddlenght >maxlength)

//偶數子字串

left =ptag;

right = ptag + 1

;

int evenlength = 0

;

while(left >= str && *right != '

\0' && *left == *right)

if(evenlength >maxlength)

ptag++;

}return

maxlength;

}

由於子字串的長度可能是奇數也可能是偶數。長度是奇數的字串是從只有乙個字元的中心向兩端延長出來,而長度為偶數的字串是從乙個有兩個字元的中心向兩端延長出來。因此程式中要把這兩種情況都考慮進去。

由於總共有o(n)個字元,每個字元可能延長o(n)次,每次延長時只需要o(1)就能判斷出是不是對稱的,因此整個函式的時間效率是o(n^2)。

上述方法稱為樸素演算法,關於字串的題目常用的演算法有kmp、字尾陣列、ac自動機,這道題目利用擴充套件kmp可以解答,其時間複雜度也很快o(n*logn)。但是,這裡介紹乙個專門針對回文子串的演算法,其時間複雜度為o(n),這就是manacher演算法。

3.manacher演算法

演算法的基本思路是這樣的:把原串每個字元中間用乙個串中沒出現過的字元分隔#開來(統一奇偶),同時為了防止越界,在字串的首部也加入乙個特殊符$,但是與分隔符不同。同時字串的末尾也加入'\0'。演算法的核心:用輔助陣列p記錄以每個字元為核心的最長回文字串半徑。也就是p[i]記錄了以str[i]為中心的最長回文字串半徑。p[i]最小為1,此時回文字串就是字串本身。

示例:原字串 'abba』,處理後的新串 ' $#a#b#b#a#\0』,得到對應的輔助陣列p=[0,1,1,2,1,2,5,2,2,1]。

程式如下,對應的變數解釋在後面

//

預處理,將str:abba轉換為: $#a#b#b#a#\0(從1開始)

char * pre(char *str)

prestr[

2*length+2]='#'

; prestr[

2*length+3]='\0'

;

return

prestr;

}

以下是manacher演算法的具體實現,包括:輔助陣列的構建、最大字串長度的獲取。

//

manacher演算法

int getmaxsym3(char *str)

else

while(prestr[i-p[i]]==prestr[i+p[i]]&&i-p[i]>0&&i+p[i]

if(i+p[i] >mx)

}//最大回文字串長度

int maxlen = 0

;

for(int i=0;i)

}delete prestr;

delete p;

return maxlen - 1

;}

上面幾個變數說明:pi記錄具有遍歷過程中最長半徑的回文字串中心字串。mx記錄了具有最長回文字串的右邊界。

pi是最長回文字串(淡藍色)的中心,如果以j為中心的最大回文串如上如所示,那麼i處的情況與j處相同(關於pi的兩側是對稱的)。這樣便減少了運算量,i的對稱位置是2*pi-i。

但是有另外一種情況,就是j的一部分超出藍色部分,這時p[i]=p[j]就不一定對了,如下圖

這就為什麼有取最小值這個操作:

if(mx>i)

剩下的**就很容易看懂了。

最後遍歷一邊p陣列,找出最大的p[i]-1就是所求的最長回文字串長度,說明如下:

(1)因為p[i]記錄插入分隔符之後的回文字串半徑,所以以i為中心的回文字串長度為2*p[i]-1。例如:bb=>#b#b#,中間#的半徑為3,回文字串長度為2*3-1;

(2)注意上面兩個串的關係。 #b#b#減去乙個#號的長度就是原來的2倍。即((2*p[i]-1)-1)/2 = p[i]-1,得證。

最長回文字串

scanf s 輸入字串碰到空格或者tab就會停下來。此處可以使用fgets或者gets 另外注意標頭檔案cctype中的函式的巧妙使用,此處使用isalpha和toupper簡化了 此處列舉字串的中間位置,然後向倆邊擴充套件,節省了時間複雜度,注意向倆邊擴充套件時,奇數個和偶數個長度的區別。另外程...

最長回文字串

回文串定義 回文串 是乙個正讀和反讀都一樣的字串,比如 asddsa 或者 lovekevol 等等就是回文串。回文子串,顧名思義,即字串中滿足回文性質的子串。這裡我給出通過 列舉回文串的中間位置i,然後不斷向外擴充套件,直達有字元不相同。注意,這裡長度為奇數和偶數的處理方式是不一樣的。下面給出 這...

最長回文字串

時間限制 1000 ms 記憶體限制 65535 kb 難度 4 描述 輸入乙個字串,求出其中最長的回文子串。子串的含義是 在原串連續出現的字串片段。回文的含義是 正著看和倒著看是相同的,如abba和abbebba。在判斷是要求忽略所有的標點和空格,且忽略大小寫,但輸出時按原樣輸出 首尾不要輸出多餘...