生活随笔
收集整理的這篇文章主要介紹了
最长回文Manacher
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
預(yù)處理:
判斷一個(gè)串是不是回文串,往往要分開編寫,造成代碼的拖沓
int LongestPalindrome(const char * s, int n){ int i, j, max; if (s == 0 || n < 1) return 0; max = 0; for (i = 0; i < n; ++i){//i is the middle point of palindrome for (j = 0; (i - j >= 0) && (i + j < n);++j)// for the lenth of palindrome is odd if (s[i - j] != s[i + j]) break; if (--j * 2 + 1>max) max = j * 2 + 1; for (j = 0; (i - j >= 0) && (i + j + 1 < n); ++j)//for the even case if (s[i - j] != s[i + j + 1]) break; if (--j * 2 + 2 > max) max = j * 2 + 2; } return max;}void LongestPalindrome_test(){ char s[] = "aba"; int length = LongestPalindrome(s,strlen(s)); cout << length << endl;}上面的循環(huán)中,對于回文長度本身的奇偶性,我們進(jìn)行區(qū)別處理。這樣有點(diǎn)拖沓。我們根據(jù)一個(gè)簡單的事實(shí):長度為n的字符串,共有n-1個(gè)“鄰接”,加上首字符的前面后某字符的后面,共有n+1的“空(gap)”。因此,字符串本身和gap一起,共有2n+1個(gè),必定是奇數(shù)。
- abba --> #a#b#b#a# 此回文這樣處理后長度4-->9
- aba --> #a#b#c# 此回文這樣處理后長度3-->7
因此,將待計(jì)算母串?dāng)U展成gap串,則里面的回文字串的長度都變成了奇數(shù),我們計(jì)算的時(shí)候只用考慮奇數(shù)匹配即可。
數(shù)組 int P[size]:
字符串12212321--> S[] = "$#1#2#2#1#2#3#2#1#";( 為了統(tǒng)一處理,最前面加一個(gè)特殊字符。則下標(biāo)從1開始)
用一個(gè)數(shù)組P[i]來記錄以字符S[i] 為中心的最長回文字串向左/向右擴(kuò)張的長度(包括S[i]),比如S和P的對應(yīng)關(guān)系:
S: "# 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #";P: {1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1}P[i]-1正好是原字符串中回文串的總長度。
“# 1 # 2 # 2 # 1 #”
“---------1 2 3 4 5”
P[i] = 5
原始回文字符串長度是偶數(shù)的情況,則在gap串中P[i]的值多了中間的一個(gè)#。P[i]-1 == length
"# 1 # 2 # 3 # 2 # 1 # "
"---------------------1 2 3 4 5 6 "
p[i] = 6
原始回文字符串長度是奇數(shù)的情況,則在gap串種P[i]的值多了最后的#號。P[i]-1 == length
目前,只要知道了數(shù)組P的值,就能求得最多長回文的大小了。但是,怎么求呢P呢?請看下文。
如何求數(shù)組P:
S: "# 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #";P: {1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1}我們的任務(wù),假定已經(jīng)得到了前i個(gè)值,考察i+1如何計(jì)算。即:在P[0...i-1]已知的情況下,利用其中信息,計(jì)算P[i]的值。
通過簡單遍歷,找到i個(gè)三元組{k,P[k],k+p[k]},0<=k<=i-1。(以k為中心的字符形成的最大回文子串的最右位置是k+p[k]-1)以k+p[k]為關(guān)鍵字,挑選出這i個(gè)三元組中,k+p[k]最大的那個(gè)三元組,不妨記作(id,P[id],id+P[id])。進(jìn)一步,為了簡化,記mx=P[id]+id,因此,得到三元組為(id,P[id],mx),這個(gè)三元組的含義非常明顯:所有i個(gè)三組中,向右達(dá)到最遠(yuǎn)的位置,就是mx。在計(jì)算P[i]的時(shí)候,考察i是否落在了區(qū)間[0,mx)中;
-1. 若i在mx的右側(cè),說明[0,mx)沒有能夠控制住i,P[0..i-1]都已知,無法給P[i]的計(jì)算帶來有價(jià)值的信息。
-2. 若i在mx的左側(cè),說明[0,mx)控制(也有可能部分控制)了i,現(xiàn)在以圖示來詳細(xì)考察這種情況。這就是Manacher遞推關(guān)系。Manacher遞推關(guān)系:
記i關(guān)于id的對稱點(diǎn)位j(j=2*id - i),若此時(shí)滿足條件mx-iP[j].
記my為mx關(guān)于id的對稱點(diǎn)();
由于以S[id]為中心的最大回文字串為S[my+1...id...mx-1],
即:S[my+1....,id]與S[id,...,mx-1]對稱,而i和j關(guān)于id對稱,因此P[i]=Pj。
記i關(guān)于id的對稱點(diǎn)位j(),若此時(shí)滿足條件mx-iP[j]:
記my為mx關(guān)于id的對稱點(diǎn)();
由于以S[id]為中心得最大回文字串為S[my+1,...id...mx-1],即S[my+1,...,id]與S[id,...,mx-1]對稱,而i和j關(guān)于id對稱,因此P[i]至少等于mx-i(途中綠框部分)
Manacher代碼
void Manacher(char * s, int *p){ int size = strlen(s); p[0] = 1; int id = 0; int mx = 1; for (int i = 1; i < size; i++){ if (mx > i) p[i] = min(p[2 * id - i], mx - i); else p[i] = 1; /* 這里計(jì)算s[i]處實(shí)際回文的長度, 如果沒有manacher算法,則這里p[i]每次都是從1開始 而manancher算法利用了P[0...i-1]的信息,讓i可能從大于1的地方開始*/ for (; s[i - p[i]] == s[i + p[i]]; p[i]++); if (mx < i + p[i]){ mx = i + p[i]; id = i; } }}void Manacher_test(){ char * s = "abbca2r"; int size = (int)strlen(s); cout << s << endl; int sizenew = size * 2 + 2;//加上開始的$????char?*?snew?=?new?char[sizenew?+?1];//別忘了最后的\0????snew[0]?=?'$'; int j = 0; for (int i = 1; i < 2 * size + 2; i++){ if (i % 2 == 0) snew[i] = s[j++]; else snew[i] = '#'; } snew[sizenew] = '\0'; cout << snew << endl; int * p = new int[sizenew]; Manacher(snew,p); Print(p,sizenew);}Manacher的改進(jìn)
p[j] > mx - i: p[i] = mx - i
p[j] < mx - i: p[i] = p[j]
p[j] = mx - i: p[i] >= p[j]
根據(jù)上面的源碼,原始Manacher算法,前兩個(gè)等號都是大于等于。
下面是改進(jìn)的Manacher算法:
void Manacher2(char * s, int * p){ int size = strlen(s); p[0] = 1; int id = 0; int mx = 1; for (int i = 1; i < size; i++){ if (mx>i){ if (mx > i){ if (p[2 * id - 1] != mx - i){ p[i] = min(p[2*id - i],mx-i); //能確定p[i]的值 } else{ p[i] = p[2*id - i]; for (; s[i - p[i]] == s[i + p[i]]; p[i]++); } } } else{ p[i] = 1; for (; s[i - p[i]] == s[i + p[i]]; p[i]++); } if (mx < i + p[i]){ mx = i + p[i]; id = i; } }}來自為知筆記(Wiz)
轉(zhuǎn)載于:https://www.cnblogs.com/gavin-yue/p/4975665.html
總結(jié)
以上是生活随笔為你收集整理的最长回文Manacher的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。