最长回文子字符串马拉车算法的C++实现

560 阅读3分钟

Manacher’s Algorithm – Linear Time Longest Palindromic Substring (LPS) in C++

Manacher’s Algorithm – Linear Time Longest Palindromic Substring

0  1  2  3  4  5  6  7  8  9  10 11 12  // index
#  a  #  b  #  a  #  a  #  b  #  a  #   // string
0  1  0  3  0  1  6  1  0  3  0  1  0   // 以该点为对称中心的对称字符串长度(不含#)

e.g. in string “abaaba”, L[3] = 3 means substring from position 0 (3-3) to 6 (3+3) is a palindrome which is “aba” of length 3, it also means that substring from index 0 [(3-3)/2] to 2 [(3+3)/2 – 1] is a palindrome which is “aba” of length 3.

0  1  2  3  4  5  6  7  8  9  10 11 12 13 14  // index
#  a  #  b  #  a  #  b  #  a  #  b  #  a  #   // string
0  1  0  3  0  5  0  7  0  5  0  3  0  1  0   // 以该点为对称中心的对称字符串长度(不含#)

If we already know LPS length values at positions 1, 2, 3, 4, 5, 6 and 7 already then we may not need to calculate LPS length at positions 8, 9, 10, 11, 12 and 13 because they are equal to LPS length values at corresponding positions on left side of position 7.

Is it always true that LPS length values around at palindromic center position are always symmetric (same)?

Answer is NO.

Look at positions 3 and 11 in string “abababa”. Both positions have LPS length 3. Immediate left and right positions are symmetric (with value 0), but not the next one. Positions 1 and 5 (around position 3) are not symmetric. Similarly, positions 9 and 13 (around position 11) are not symmetric.

0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#  b  #  a  #  b  #  c  #  b  #  a  #  b  #  c  #  b  #  a  #  c  #  c  #  b  #  a  #
0  1  0  3  0  1  0  7  0  1  0  9  0  1  0  5  0  1  0  1  0  1  2  1  0  1  0  1  0
#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>
#include<string>
#include <stdio.h> 
#include <string.h> 
using namespace std;


// A C program to implement Manacher’s Algorithm

void findLongestPalindromicString(string &text)
{
    int N = text.size();
    if (N == 0)
        return;
    N = 2 * N + 1; // Position count
    vector<int> L(N,0); //LPS Length Array
    L[0] = 0;
    L[1] = 1;
    int C = 1; // centerPosition
    int R = 2; // centerRightPosition
    int i = 0; // currentRightPosition
    int iMirror; // currentLeftPosition
    int expand = -1;
    int diff = -1;
    int maxLPSLength = 1;
    int maxLPSCenterPosition = 0;
    int start = -1;
    int end = -1;

    // Uncomment it to print LPS Length array
    // printf("%d %d ", L[0], L[1]);
    for (i = 2; i < N; i++)
    {
        // get currentLeftPosition iMirror for currentRightPosition i
        // currentRightPosition + currentLeftPosition = 2 * centerPosition
        // i.e., i + iMirror = 2 * C
        iMirror = 2 * C - i;
        // Reset expand - means no expansion required
        expand = 0;
        diff = R - i; // centerRightPosition - currentRightPosition
        // If currentRightPosition i is within centerRightPosition R
        if (diff >= 0)
        {
            if (L[iMirror] < diff) // Case 1
                L[i] = L[iMirror];
            else if (L[iMirror] == diff && R == N - 1) // Case 2
                L[i] = L[iMirror];
            else if (L[iMirror] == diff && R < N - 1)  // Case 3
            {
                L[i] = L[iMirror];
                expand = 1;  // expansion required
            }
            else if (L[iMirror] > diff)  // Case 4
            {
                L[i] = diff;
                expand = 1;  // expansion required
            }
        }
        else
        {
            L[i] = 0;
            expand = 1;  // expansion required
        }

        if (expand == 1)
        {
            //Attempt to expand palindrome centered at currentRightPosition i
            //Here for odd positions, we compare characters and
            //if match then increment LPS Length by ONE
            //If even position, we just increment LPS by ONE without
            //any character comparison
            while (((i + L[i]) < N && (i - L[i]) > 0) &&
                (((i + L[i] + 1) % 2 == 0) ||
                (text[(i + L[i] + 1) / 2] == text[(i - L[i] - 1) / 2])))
            {
                L[i]++;
            }
        }

        if (L[i] > maxLPSLength)  // Track maxLPSLength
        {
            maxLPSLength = L[i];
            maxLPSCenterPosition = i;
        }

        // If palindrome centered at currentRightPosition i
        // expand beyond centerRightPosition R,
        // adjust centerPosition C based on expanded palindrome.
        if (i + L[i] > R)
        {
            C = i;
            R = i + L[i];
        }
        //Uncomment it to print LPS Length array
        //printf("%d ", L[i]);
    }
    //printf("\n");
    start = (maxLPSCenterPosition - maxLPSLength) / 2;
    end = start + maxLPSLength - 1;
    //printf("start: %d end: %d\n", start, end);
    printf("LPS of string is %s : ", text.c_str());
    for (i = start; i <= end; i++)
        printf("%c", text[i]);
    printf("\n");
}


int main(int argc, char* argv[])
{
    string text("babcbabcbaccba");
    findLongestPalindromicString(text);
    return 0;
}

leetcode 5. 最长回文子串

class Solution {
public:
    string longestPalindrome(string s) {
        if(s.size()==0)return s;
        // Manacher算法
        int N=s.size();
        N=2*N+1;
        vector<int> Lps(N,0);
        Lps[1]=1;
        int C=1,R=2,i=0,iMirror,expand=-1,diff=-1;
        int maxLPSlength=1,maxLPSCenterPosition=0;
        
        for(int i=2;i<N;i++)
        {
            iMirror=2*C-i;
            diff=R-i;
            expand=0;
            if(diff>=0)
            {
                Lps[i]=min(Lps[iMirror],diff);
                if(Lps[iMirror]>diff || (Lps[iMirror]==diff && R<N-1))
                {
                    expand=1;
                }
            }
            else{
                Lps[i]=0;
                expand=1;
            }

            if(expand==1)
            {
                while(
                    ((i+Lps[i])<N && (i-Lps[i])>0) &&  // (i+Lps[i]+1)<N
                    (((i+Lps[i]+1)%2==0) || (s[(i+Lps[i]+1)/2]==s[(i-Lps[i]-1)/2]))
                ) Lps[i]++;
            }

            if(Lps[i]>maxLPSlength)
            {
                maxLPSlength=Lps[i];
                maxLPSCenterPosition=i;
            }

            if(i+Lps[i]>R)
            {
                C=i;
                R=i+Lps[i];
            }
        }
        return s.substr((maxLPSCenterPosition-maxLPSlength)/2,maxLPSlength);
    }
};

leetcode 647. 回文子串

class Solution {
public:
    int countSubstrings(string s) {
        if(s.size()<2)return s.size();
        // Manacher
        int N=s.size();
        N=2*N+1;
        vector<int> lps(N,0);
        int C=1,R=2,i=0,iMirror,expand=-1,diff=-1;
        lps[1]=1;
        int count=1;
        for(int i=2;i<N;i++)
        {
            iMirror=2*C-i;
            diff=R-i;
            expand=0;
            if(diff>=0)
            {
                lps[i]=min(lps[iMirror],diff);
                if(lps[iMirror]>diff || (lps[iMirror]==diff && R<N-1))
                {
                    expand=1;
                }
            }
            else{
                lps[i]=0;
                expand=1;
            }
            if(expand==1)
            {
                while(
                    (i+lps[i]<N && i-lps[i]>0) &&
                    ((i+lps[i]+1) % 2 ==0 || 
                    s[(i+lps[i]+1)/2]==s[(i-lps[i]-1)/2])
                )lps[i]++;
            }
            if(i+lps[i]>R)
            {
                C=i;
                R=i+lps[i];
            }
            if(lps[i]>0)count+=(lps[i]+1)/2;
        }
        return count;
    }
};