題目背景算法
給定一個字符串str,若子串s是迴文串(如aba, abba),則稱s爲str的迴文子串,如今須要求出str的最長迴文子串數組
解法ide
1.枚舉法
spa
這種解法比暴力法好一點,就是遍歷字符串每一位str[i]:blog
(1)首先若是是aba型,那就從i出發往兩邊擴張,看str[i-1]跟str[i+1]是否相等,進而擴張到str[i-k]跟str[i+k],直到str[i-(k+1)]跟str[i+(k+1)]不相等,注意這個擴張的前提的不越界,這樣子就能獲得最長長度是1+2*k字符串
(2)再者還得討論abba型,那就從i,i+1分別出發往兩邊擴張,看str[i-1]跟str[i+1+1]是否相等,進而擴張到str[i-k]跟str[i+1+k],直到str[i-(k+1)]跟str[i+1+(k+1)]不相等,注意這個擴張的前提的不越界,這樣子就能獲得最長長度是2+2*kget
取(1)和(2)中較大的值,最後綜合全部的i,取出最最大的值就是最長迴文子串it
這種解法的時間複雜度是O(n2),可是還存在一種線性的時間複雜度算法class
2.Manacher算法遍歷
首先須要對字符串進行預處理,目的是要統一aba和abba的解法,作法是在每一個字符串間隙和頭尾都加上#
如 a b b a --> # a # b # b # a #
這樣生成新的字符串S,它必定是個奇數,經過有字母的字符位能夠計算aba的狀況,經過#的字符位能夠計算abba的狀況,這樣就很好把兩種狀況統一了
定義一個數組P[i],來記錄以字符S[i]爲中心的最長迴文子串向左/右擴張的長度(包括S[i]),即單邊的範圍
接下來須要考慮的是已經P[0],P[1]...P[i-1]的狀況下怎麼獲得P[i]
從0...i-1中取一個id,使P[id]+id最大,記作mx=P[id]+id,這個mx顯然是以前的最長迴文子串可以到達的最右端的範圍
如今就考察i有沒有落在[0,mx)區間,若是沒落在那就說明以前的P[0],P[1]...P[i-1]都沒法幫助計算P[i],須要用枚舉法計算,若是落在,那就說明P[0],P[1]...P[i-1]至少是有價值的,進一步能夠分類討論:
設i關於id的對稱點爲j,知足j=2*id-i,設mx關於id的對稱點my,知足my=2*k-mx
若(1)mx-i>P[j],也即j-my>P[j],這種狀況就是迴文裏嵌迴文,因此直接可得P[i]=P[j]
(2)mx-i<P[j],也即j-my<P[j],這種狀況是部分迴文裏嵌迴文,沒法直接獲得P[i],但至少能夠得知P[i]>mx-i,這樣也能夠省下很多計算,直接從mx-i開始用枚舉法往外擴張求得最後值
最後貼一下代碼
void Manacher(const char* str) { int size = strlen(str); int N = 2*size+1; int* p = new int[N]; int mx = 1; int id = 0; p[0] = 1; for(int i = 1; i < N; i++) { if(i < mx) p[i] = min(p[2*id-i], mx-i); else p[i] = 1; while((i != p[i]) && (((i+p[i]) % 2 == 1) || (str[(i+p[i])/2-1] == str[(i-p[i])/2-1]))) p[i]++; if(mx < i + p[i]) { mx = i + p[i]; id = i; } } Print(str); Print(p, N); delete[] p; }