日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最长回文Manacher

發(fā)布時(shí)間:2023/12/10 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最长回文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ò),歡迎將生活随笔推薦給好友。