Manacher's algorithm (algorithm to find longest palindrome substring in linear time)

前端 未结 10 489
走了就别回头了
走了就别回头了 2020-12-22 16:02

After spending about 6-8 hours trying to digest the Manacher\'s algorithm, I am ready to throw in the towel. But before I do, here is one last shot in the dark: can anyone e

相关标签:
10条回答
  • 2020-12-22 16:37

    This material is of great help for me to understand it: http://solutionleetcode.blogspot.com/2013/07/leetcode-longest-palindromic-substring.html

    Define T as the length of the longest palindromic substrings centered at each of the characters.

    The key thing is, when smaller palindromes are completely embedded within the longer palindrome, T[i] should also be symmetric within the longer palindrome.

    Otherwise, we will have to compute T[i] from scratch, rather than induce from the symmetric left part.

    0 讨论(0)
  • 2020-12-22 16:38
    using namespace std;
    
    class Palindrome{
    public:
        Palindrome(string st){
            s = st; 
            longest = 0; 
            maxDist = 0;
            //ascii: 126(~) - 32 (space) = 94 
            // for 'a' to 'z': vector<vector<int>> v(26,vector<int>(0)); 
            vector<vector<int>> v(94,vector<int>(0)); //all ascii 
            mDist.clear();
            vPos = v; 
            bDebug = true;
        };
    
        string s;
        string sPrev;    //previous char
        int longest;     //longest palindrome size
        string sLongest; //longest palindrome found so far
        int maxDist;     //max distance been checked 
        bool bDebug;
    
        void findLongestPal();
        int checkIfAnchor(int iChar, int &i);
        void checkDist(int iChar, int i);
    
        //store char positions in s pos[0] : 'a'... pos[25] : 'z' 
        //       0123456
        // i.e. "axzebca" vPos[0][0]=0  (1st. position of 'a'), vPos[0][1]=6 (2nd pos. of 'a'), 
        //                vPos[25][0]=2 (1st. pos. of 'z').  
        vector<vector<int>> vPos;
    
        //<anchor,distance to check> 
        //   i.e.  abccba  anchor = 3: position of 2nd 'c', dist = 3 
        //   looking if next char has a dist. of 3 from previous one 
        //   i.e.  abcxcba anchor = 4: position of 2nd 'c', dist = 4 
        map<int,int> mDist;
    };
    
    //check if current char can be an anchor, if so return next distance to check (3 or 4)
    // i.e. "abcdc" 2nd 'c' is anchor for sub-palindrome "cdc" distance = 4 if next char is 'b'
    //      "abcdd: 2nd 'd' is anchor for sub-palindrome "dd"  distance = 3 if next char is 'c'
    int Palindrome::checkIfAnchor(int iChar, int &i){
        if (bDebug)
              cout<<"checkIfAnchor. i:"<<i<<" iChar:"<<iChar<<endl;
        int n = s.size();
        int iDist = 3;
        int iSize = vPos[iChar].size();
        //if empty or distance to closest same char > 2
        if ( iSize == 0 || vPos[iChar][iSize - 1] < (i - 2)){
            if (bDebug)
                  cout<<"       .This cannot be an anchor! i:"<<i<<" : iChar:"<<iChar<<endl; 
            //store char position
            vPos[iChar].push_back(i);
            return -1; 
        }
    
        //store char position of anchor for case "cdc"
        vPos[iChar].push_back(i);    
        if (vPos[iChar][iSize - 1] == (i - 2))
            iDist = 4;
        //for case "dd" check if there are more repeated chars
        else {
            int iRepeated = 0;
            while ((i+1) < n && s[i+1] == s[i]){
                i++;
                iRepeated++;
                iDist++; 
                //store char position
                vPos[iChar].push_back(i);
            }
        }
    
        if (bDebug)
              cout<<"       .iDist:"<<iDist<<" i:"<<i<<endl; 
    
        return iDist;
    };
    
    //check distance from previous same char, and update sLongest
    void Palindrome::checkDist(int iChar, int i){
        if (bDebug)
                cout<<"CheckDist. i:"<<i<<" iChar:"<<iChar<<endl;
        int iDist;
        int iSize = vPos[iChar].size();
        bool b1stOrLastCharInString; 
        bool bDiffDist;
    
        //checkAnchor will add this char position 
        if ( iSize == 0){
            if (bDebug)
                cout<<"       .1st time we see this char. Assign it INT_MAX Dist"<<endl; 
            iDist = INT_MAX;
        }
        else {
            iDist = i - vPos[iChar][iSize - 1]; 
        }
    
        //check for distances being check, update them if found or calculate lengths if not.
        if (mDist.size() == 0) {
            if (bDebug)
                 cout<<"       .no distances to check are registered, yet"<<endl;
            return;
        }
    
        int i2ndMaxDist = 0;
        for(auto it = mDist.begin(); it != mDist.end();){
            if (bDebug)
                    cout<<"       .mDist. anchor:"<<it->first<<" . dist:"<<it->second<<endl; 
            b1stOrLastCharInString = false; 
            bDiffDist = it->second == iDist;  //check here, because it can be updated in 1st. if
            if (bDiffDist){
                if (bDebug)
                    cout<<"       .Distance checked! :"<<iDist<<endl;
                //check if it's the first char in the string
                if (vPos[iChar][iSize - 1] == 0 || i == (s.size() - 1))
                    b1stOrLastCharInString = true;
                //we will continue checking for more...
                else {
                    it->second += 2; //update next distance to check
                    if (it->second > maxDist) {
                         if (bDebug)
                              cout<<"       .previous MaxDist:"<<maxDist<<endl;
                         maxDist = it->second;
                         if (bDebug)
                              cout<<"       .new MaxDist:"<<maxDist<<endl;
                    }
                    else if (it->second > i2ndMaxDist) {//check this...hmtest
                         i2ndMaxDist = it->second;
                         if (bDebug)
                              cout<<"       .second MaxDist:"<<i2ndMaxDist<<endl;
                    }
                    it++; 
                }
            }
            if (!bDiffDist || b1stOrLastCharInString) {
                if (bDebug && it->second != iDist)
                    cout<<"       .Dist diff. Anchor:"<<it->first<<" dist:"<<it->second<<" iDist:"<<iDist<<endl;
                else if (bDebug) 
                    cout<<"       .Palindrome found at the beggining or end of the string"<<endl;
    
                //if we find a closest same char.
                if (!b1stOrLastCharInString && it->second > iDist){
                    if (iSize > 1) {
                       if (bDebug)
                           cout<<"       . < Dist . looking further..."<<endl; 
                       iSize--;  
                       iDist = i - vPos[iChar][iSize - 1];
                       continue;    
                    }
                }
                if (iDist == maxDist) {
                    maxDist = 0;
                    if (bDebug)
                         cout<<"       .Diff. clearing Max Dist"<<endl;
                }
                else if (iDist == i2ndMaxDist) {
                    i2ndMaxDist = 0;
                    if (bDebug)
                          cout<<"       .clearing 2nd Max Dist"<<endl; 
                }
    
                int iStart; 
                int iCurrLength;
                //first char in string
                if ( b1stOrLastCharInString && vPos[iChar].size() > 0 && vPos[iChar][iSize - 1] == 0){
                    iStart = 0;
                    iCurrLength = i+1;
                }
                //last char in string
                else if (b1stOrLastCharInString && i == (s.size() - 1)){
                    iStart = i - it->second; 
                    iCurrLength = it->second + 1;
                }
                else {
                    iStart = i - it->second + 1; 
                    iCurrLength = it->second - 1;  //"xabay" anchor:2nd. 'a'. Dist from 'y' to 'x':4. length 'aba':3
                }
    
                if (iCurrLength > longest){
                    if (bDebug)
                          cout<<"       .previous Longest!:"<<sLongest<<" length:"<<longest<<endl;
                    longest = iCurrLength;               
                    sLongest = s.substr(iStart, iCurrLength);
    
                    if (bDebug)
                          cout<<"       .new Longest!:"<<sLongest<<" length:"<<longest<<endl;
                }
    
                if (bDebug)
                      cout<<"       .deleting iterator for anchor:"<<it->first<<" dist:"<<it->second<<endl; 
    
                mDist.erase(it++);
            }
        }
    
    
        //check if we need to get new max distance
        if (maxDist == 0 && mDist.size() > 0){ 
            if (bDebug)
                  cout<<"       .new maxDist needed";
            if (i2ndMaxDist > 0) {
                maxDist = i2ndMaxDist;
                if (bDebug)
                  cout<<"       .assigned 2nd. max Dist to max Dist"<<endl;
            }
            else {
                for(auto it = mDist.begin(); it != mDist.end(); it++){
                    if (it->second > maxDist)
                        maxDist = it->second;
                }
                if (bDebug)
                  cout<<"       .new max dist assigned:"<<maxDist<<endl;
            }
        }  
    };        
    
    void Palindrome::findLongestPal(){
        int n = s.length(); 
        if (bDebug){
            cout<<"01234567891123456789212345"<<endl<<"abcdefghijklmnopqrstuvwxyz"<<endl<<endl;            
            for (int i = 0; i < n;i++){
                if (i%10 == 0)
                    cout<<i/10;
                else
                    cout<<i;
            }
            cout<<endl<<s<<endl;
        }
        if (n == 0)
            return;
    
        //process 1st char
        int j = 0;
        //for 'a' to 'z' : while (j < n && (s[j] < 'a' && s[j] > 'z'))
        while (j < n && (s[j] < ' ' && s[j] > '~'))
            j++;
        if (j > 0){
            s.substr(j);
            n = s.length();
        }
        // for 'a' to 'z' change size of vector from 94 to 26 : int iChar = s[0] - 'a';
        int iChar = s[0] - ' ';
        //store char position
        vPos[iChar].push_back(0);  
    
        for (int i = 1; i < n; i++){
            if (bDebug)
                cout<<"findLongestPal. i:"<<i<<" "<<s.substr(0,i+1)<<endl; 
            //if max. possible palindrome would be smaller or equal 
            //             than largest palindrome found then exit
            //   (n - i) = string length to check 
            //   maxDist: max distance to check from i 
            int iPossibleLongestSize = maxDist + (2 * (n - i));
            if ( iPossibleLongestSize <= longest){
                if (bDebug)
                    cout<<"       .PosSize:"<<iPossibleLongestSize<<" longest found:"<<iPossibleLongestSize<<endl;
                return;
            }
    
            //for 'a' to 'z' : int iChar = s[i] - 'a';
            int iChar = s[i] - ' ';
            //for 'a' to 'z': if (iChar < 0 || iChar > 25){
            if (iChar < 0 || iChar > 94){
                if (bDebug)
                    cout<<"       . i:"<<i<<" iChar:"<<s[i]<<" skipped!"<<endl;
                continue;
            }
    
            //check distance to previous char, if exist
            checkDist(iChar, i);
    
            //check if this can be an anchor
            int iDist = checkIfAnchor(iChar,i);
            if (iDist == -1) 
                continue;
    
            //append distance to check for next char.
            if (bDebug)
                    cout<<"       . Adding anchor for i:"<<i<<" dist:"<<iDist<<endl;
            mDist.insert(make_pair(i,iDist));
    
            //check if this is the only palindrome, at the end
            //i.e. "......baa" or "....baca" 
            if (i == (s.length() - 1) && s.length() > (iDist - 2)){
                //if this is the longest palindrome! 
                if (longest < (iDist - 1)){
                    sLongest = s.substr((i - iDist + 2),(iDist - 1));
                }
            }
        }
    };
    
    int main(){
        string s;
        cin >> s;
    
        Palindrome p(s);
        p.findLongestPal();
        cout<<p.sLongest; 
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-22 16:41
    class Palindrome
    {
        private int center;
        private int radius;
    
        public Palindrome(int center, int radius)
        {
            if (radius < 0 || radius > center)
                throw new Exception("Invalid palindrome.");
    
            this.center = center;
            this.radius = radius;
        }
    
        public int GetMirror(int index)
        {
            int i = 2 * center - index;
    
            if (i < 0)
                return 0;
    
            return i;
        }
    
        public int GetCenter()
        {
            return center;
        }
    
        public int GetLength()
        {
            return 2 * radius;
        }
    
        public int GetRight()
        {
            return center + radius;
        }
    
        public int GetLeft()
        {
            return center - radius;
        }
    
        public void Expand()
        {
            ++radius;
        }
    
        public bool LargerThan(Palindrome other)
        {
            if (other == null)
                return false;
    
            return (radius > other.radius);
        }
    
    }
    
    private static string GetFormatted(string original)
    {
        if (original == null)
            return null;
        else if (original.Length == 0)
            return "";
    
        StringBuilder builder = new StringBuilder("#");
        foreach (char c in original)
        {
            builder.Append(c);
            builder.Append('#');
        }
    
        return builder.ToString();
    }
    
    private static string GetUnFormatted(string formatted)
    {
        if (formatted == null)
            return null;
        else if (formatted.Length == 0)
            return "";
    
        StringBuilder builder = new StringBuilder();
        foreach (char c in formatted)
        {
            if (c != '#')
                builder.Append(c);
        }
    
        return builder.ToString();
    }
    
    public static string FindLargestPalindrome(string str)
    {
        string formatted = GetFormatted(str);
    
        if (formatted == null || formatted.Length == 0)
            return formatted;
    
        int[] radius = new int[formatted.Length];
    
        try
        {
            Palindrome current = new Palindrome(0, 0);
            for (int i = 0; i < formatted.Length; ++i)
            {
                radius[i] = (current.GetRight() > i) ?
                    Math.Min(current.GetRight() - i, radius[current.GetMirror(i)]) : 0;
    
                current = new Palindrome(i, radius[i]);
    
                while (current.GetLeft() - 1 >= 0 && current.GetRight() + 1 < formatted.Length &&
                    formatted[current.GetLeft() - 1] == formatted[current.GetRight() + 1])
                {
                    current.Expand();
                    ++radius[i];
                }
            }
    
            Palindrome largest = new Palindrome(0, 0);
            for (int i = 0; i < radius.Length; ++i)
            {
                current = new Palindrome(i, radius[i]);
                if (current.LargerThan(largest))
                    largest = current;
            }
    
            return GetUnFormatted(formatted.Substring(largest.GetLeft(), largest.GetLength()));
        }
        catch (Exception ex) 
        {
            Console.WriteLine(ex);
        }
    
        return null;
    }
    
    0 讨论(0)
  • 2020-12-22 16:43

    I have found one of the best explanation so far at the following link:

    http://tarokuriyama.com/projects/palindrome2.php

    It also has a visualization for the same string example (babcbabcbaccba) used at the first link mentioned in the question.

    Apart from this link, i also found the code at

    http://algs4.cs.princeton.edu/53substring/Manacher.java.html

    I hope it will be helpful to others trying hard to understand the crux of this algorithm.

    0 讨论(0)
提交回复
热议问题