最長迴文子串 用manacher算法



題目1528:最長迴文子串

時間限制:1 秒

內存限制:128 兆

特殊判題:

提交:210

解決:77

題目描述:

迴文串就是一個正讀和反讀都一樣的字符串,比如“level”或者“noon”等等就是迴文串。
迴文子串,顧名思義,即字符串中滿足迴文性質的子串。
給出一個只由小寫英文字符a,b,c...x,y,z組成的字符串,請輸出其中最長的迴文子串的長度。

輸入:

輸入包含多個測試用例,每組測試用例輸入一行由小寫英文字符a,b,c...x,y,z組成的字符串,字符串的長度不大於200000。

輸出:

對於每組測試用例,輸出一個整數,表示該組測試用例的字符串中所包含的的最長迴文子串的長度。

樣例輸入:
abab
bbbb
abba
樣例輸出:
3
4
4

算法分析

     manacher 算法可以參考http://blog.csdn.net/ggggiqnypgjg/article/details/6645824

      首先:大家都知道什麼叫回文串吧,這個算法要解決的就是一個字符串中最長的迴文子串有多長。這個算法可以在On)的時間複雜度內既線性時間複雜度的情況下,求出以每個字符爲中心的最長迴文有多長,
    這個算法有一個很巧妙的地方,它把奇數的迴文串和偶數的迴文串統一起來考慮了。這一點一直是在做迴文串問題中時比較煩的地方。這個算法還有一個很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重複匹配。
    算法大致過程是這樣。先在每兩個相鄰字符中間插入一個分隔符,當然這個分隔符要在原串中沒有出現過。一般可以用‘#’分隔。這樣就非常巧妙的將奇數長度迴文串與偶數長度迴文串統一起來考慮了(見下面的一個例子,迴文串長度全爲奇數了),然後用一個輔助數組P記錄以每個字符爲中心的最長迴文串半徑的信息。Pid]記錄的是以字符strid]爲中心的最長迴文串的半徑,當以strid]爲第一個字符,這個最長迴文串向右延伸了Pid]個字符。
    原串:    w aa bwsw f d
    新串:   # w# a # a # b# w # s # w # f # d #
輔助數組P:  1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1
    這裏有一個很好的性質,Pid-1就是該回文子串在原串中的長度(包括‘#’)


   怎麼在線性時間內求P[i]呢,我們還要用到一個輔助的mx, 表示i之前 迴文串 所能影響到的最右邊的位置,用id記錄mx所屬的迴文串的id.

    算法關鍵爲

        if(mx>i){
            mLen[i] = max(mLen[2*id-i],mx-i+1);
        }

     

    

華麗的分割

————————————————————————————————————————————————

      第一種情況  mx>i+p[i]

      由於以id爲中心的 迴文字符串 左右兩邊是對稱的,所以 以i爲中心的迴文字符串和以2*id-1(也即是 i以id爲中心的對稱點)的迴文字符串是一樣的。如下圖:

      


        第二種情況,mx<=i+p[i]

        由p[id]和mx,我們僅能推斷出p[i]至少爲mx-i, mx後面的還沒有比較,需要進一步比較確定。

       

       

        求得p[i]後我們要記得更新一下mx,id.

//============================================================================
// Name        : judo1252Manacher.cpp
// Author      : wdy
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
  
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;
  
void Manach(std::string &s){
    std::vector<char> ns;
    ns.push_back('$');
    ns.push_back('#');
  
    int len = s.size();
    for(int i = 0;i<len;i++){// become odd
        ns.push_back(s.at(i));
        ns.push_back('#');
    }
  
    len = ns.size();
    int *mLen = new int[len];   //record max radius of sub string in the middle of id
    int mx = 0;
    int id = 0;
    int maxLen = 0;
  
    for(int i = 0;i<len;i++){
        mLen[i] = 1;
        if(mx>i){
            mLen[i] = max(mLen[2*id-i],mx-i+1);
        }
  
        while((i+mLen[i])<len && i-mLen[i]>0 && ns.at(i+mLen[i])==ns.at(i-mLen[i]))
            mLen[i]++;
  
        if(i + mLen[i]-1 >mx){      //update mx,id
            mx = i + mLen[i]-1 >mx;
            id = i;
        }
  
        if(mLen[i]>maxLen)              //update maxLen
            maxLen = mLen[i];
    }
  
    std::cout<<maxLen-1<<std::endl;
  
}
  
void judo(){
    std::string s;
    while(std::cin>>s){
        Manach(s);
    }
}
  
int main() {
    judo();
    //cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
    return 0;
}
  
/**************************************************************
    Problem: 1252
    User: KES
    Language: C++
    Result: Accepted
    Time:0 ms
    Memory:1520 kb
****************************************************************/
/**************************************************************
    Problem: 1528
    User: KES
    Language: C++
    Result: Accepted
    Time:150 ms
    Memory:7492 kb
****************************************************************/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章