📜  Manacher算法-线性时间最长回文子串-第4部分

📅  最后修改于: 2021-05-04 14:37:50             🧑  作者: Mango

在Manacher的算法第1部分和第2部分中,我们介绍了一些基础知识,了解LPS长度数组以及如何基于四种情况有效地计算长度数组。在第3部分中,我们实现了相同的功能。
在这里,我们将再次审查这四种情况,并尝试以不同的方式看待并实施相同的情况。
这四种情况均取决于currentLeftPosition(L [iMirror])的LPS长度值和(centerRightPosition – currentRightPosition)的值,即(R – i)。这两个信息是众所周知的,这有助于我们重用以前的可用信息并避免不必要的字符比较。
Manacher算法-线性时间最长回文子串

如果我们看一下这四种情况中,我们会看到,我们1个集合最小L [iMirror]RiL[I],然后我们尝试展开任何一个情况下,它可以扩大回文。

鉴于人们了解LPS长度数组,位置,索引,对称性等,上述观察可能看起来更直观,更易于理解和实现。

C/C++
// A C program to implement Manacher’s Algorithm
#include 
#include 
  
char text[100];
int min(int a, int b)
{
    int res = a;
    if(b < a)
        res = b;
    return res;
}
  
void findLongestPalindromicString()
{
    int N = strlen(text);
    if(N == 0)
        return;
    N = 2*N + 1; //Position count
    int L[N]; //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 maxLPSLength = 0;
    int maxLPSCenterPosition = 0;
    int start = -1;
    int end = -1;
    int diff = -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
        iMirror  = 2*C-i;
        L[i] = 0;
        diff = R - i;
        //If currentRightPosition i is within centerRightPosition R
        if(diff > 0)
            L[i] = min(L[iMirror], diff);
  
        //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("LPS of string is %s : ", text);
    for(i=start; i<=end; i++)
        printf("%c", text[i]);
    printf("\n");
}
  
int main(int argc, char *argv[])
{
  
    strcpy(text, "babcbabcbaccba");
    findLongestPalindromicString();
  
    strcpy(text, "abaaba");
    findLongestPalindromicString();
  
    strcpy(text, "abababa");
    findLongestPalindromicString();
  
    strcpy(text, "abcbabcbabcba");
    findLongestPalindromicString();
  
    strcpy(text, "forgeeksskeegfor");
    findLongestPalindromicString();
  
    strcpy(text, "caba");
    findLongestPalindromicString();
  
    strcpy(text, "abacdfgdcaba");
    findLongestPalindromicString();
  
    strcpy(text, "abacdfgdcabba");
    findLongestPalindromicString();
  
    strcpy(text, "abacdedcaba");
    findLongestPalindromicString();
  
    return 0;
}


Java
// Java program to implement Manacher's Algorithm 
import java.util.*;
  
class GFG 
{
    static void findLongestPalindromicString(String text) 
    {
        int N = text.length();
        if (N == 0)
            return;
        N = 2 * N + 1; // Position count
        int[] L = new int[N + 1]; // 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 maxLPSLength = 0;
        int maxLPSCenterPosition = 0;
        int start = -1;
        int end = -1;
        int diff = -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
            iMirror = 2 * C - i;
            L[i] = 0;
            diff = R - i;
  
            // If currentRightPosition i is within 
            // centerRightPosition R
            if (diff > 0)
                L[i] = Math.min(L[iMirror], diff);
  
            // 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]) + 1 < N && (i - L[i]) > 0) && 
                               (((i + L[i] + 1) % 2 == 0) || 
                         (text.charAt((i + L[i] + 1) / 2) == 
                          text.charAt((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]);
        }
  
        start = (maxLPSCenterPosition - maxLPSLength) / 2;
        end = start + maxLPSLength - 1;
        System.out.printf("LPS of string is %s : ", text);
        for (i = start; i <= end; i++)
            System.out.print(text.charAt(i));
        System.out.println();
    }
  
    // Driver Code
    public static void main(String[] args)
    {
        String text = "babcbabcbaccba";
        findLongestPalindromicString(text);
  
        text = "abaaba";
        findLongestPalindromicString(text);
  
        text = "abababa";
        findLongestPalindromicString(text);
  
        text = "abcbabcbabcba";
        findLongestPalindromicString(text);
  
        text = "forgeeksskeegfor";
        findLongestPalindromicString(text);
  
        text = "caba";
        findLongestPalindromicString(text);
  
        text = "abacdfgdcaba";
        findLongestPalindromicString(text);
  
        text = "abacdfgdcabba";
        findLongestPalindromicString(text);
  
        text = "abacdedcaba";
        findLongestPalindromicString(text);
    }
}
  
// This code is contributed by
// sanjeev2552


Python
# Python program to implement Manacher's Algorithm
   
def findLongestPalindromicString(text):
    N = len(text)
    if N == 0:
        return
    N = 2*N+1    # Position count
    L = [0] * N
    L[0] = 0
    L[1] = 1
    C = 1     # centerPosition
    R = 2     # centerRightPosition
    i = 0    # currentRightPosition
    iMirror = 0     # currentLeftPosition
    maxLPSLength = 0
    maxLPSCenterPosition = 0
    start = -1
    end = -1
    diff = -1
   
    # Uncomment it to print LPS Length array
    # printf("%d %d ", L[0], L[1]);
    for i in xrange(2,N):
       
        # get currentLeftPosition iMirror for currentRightPosition i
        iMirror = 2*C-i
        L[i] = 0
        diff = R - i
        # If currentRightPosition i is within centerRightPosition R
        if diff > 0:
            L[i] = min(L[iMirror], diff)
   
        # 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
        try:
            while ((i + L[i]) < N and (i - L[i]) > 0) and \
                    (((i + L[i] + 1) % 2 == 0) or \
                    (text[(i + L[i] + 1) / 2] == text[(i - L[i] - 1) / 2])):
                L[i]+=1
        except Exception as e:
            pass
   
        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]);
    start = (maxLPSCenterPosition - maxLPSLength) / 2
    end = start + maxLPSLength - 1
    print "LPS of string is " + text + " : ",
    print text[start:end+1],
    print "\n",
   
# Driver program
text1 = "babcbabcbaccba"
findLongestPalindromicString(text1)
   
text2 = "abaaba"
findLongestPalindromicString(text2)
   
text3 = "abababa"
findLongestPalindromicString(text3)
   
text4 = "abcbabcbabcba"
findLongestPalindromicString(text4)
   
text5 = "forgeeksskeegfor"
findLongestPalindromicString(text5)
   
text6 = "caba"
findLongestPalindromicString(text6)
   
text7 = "abacdfgdcaba"
findLongestPalindromicString(text7)
   
text8 = "abacdfgdcabba"
findLongestPalindromicString(text8)
   
text9 = "abacdedcaba"
findLongestPalindromicString(text9)
   
# This code is contributed by BHAVYA JAIN


C#
// C# program to implement Manacher's Algorithm
using System;
  
class GFG 
{
    static void findLongestPalindromicString(String text) 
    {
        int N = text.Length;
        if (N == 0)
            return;
        N = 2 * N + 1; // Position count
        int[] L = new int[N + 1]; // 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 maxLPSLength = 0;
        int maxLPSCenterPosition = 0;
        int start = -1;
        int end = -1;
        int diff = -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
            iMirror = 2 * C - i;
            L[i] = 0;
            diff = R - i;
  
            // If currentRightPosition i is within 
            // centerRightPosition R
            if (diff > 0)
                L[i] = Math.Min(L[iMirror], diff);
  
            // 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]) + 1 < 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]);
        }
  
        start = (maxLPSCenterPosition - maxLPSLength) / 2;
        end = start + maxLPSLength - 1;
        Console.Write("LPS of string is {0} : ", text);
        for (i = start; i <= end; i++)
            Console.Write(text[i]);
        Console.WriteLine();
    }
  
    // Driver Code
    public static void Main(String[] args)
    {
        String text = "babcbabcbaccba";
        findLongestPalindromicString(text);
  
        text = "abaaba";
        findLongestPalindromicString(text);
  
        text = "abababa";
        findLongestPalindromicString(text);
  
        text = "abcbabcbabcba";
        findLongestPalindromicString(text);
  
        text = "forgeeksskeegfor";
        findLongestPalindromicString(text);
  
        text = "caba";
        findLongestPalindromicString(text);
  
        text = "abacdfgdcaba";
        findLongestPalindromicString(text);
  
        text = "abacdfgdcabba";
        findLongestPalindromicString(text);
  
        text = "abacdedcaba";
        findLongestPalindromicString(text);
    }
}
  
// This code is contributed by PrinciRaj1992


输出:

LPS of string is babcbabcbaccba : abcbabcba
LPS of string is abaaba : abaaba
LPS of string is abababa : abababa
LPS of string is abcbabcbabcba : abcbabcbabcba
LPS of string is forgeeksskeegfor : geeksskeeg
LPS of string is caba : aba
LPS of string is abacdfgdcaba : aba
LPS of string is abacdfgdcabba : abba
LPS of string is abacdedcaba : abacdedcaba

其他方法
我们在这里讨论了两种方法。在第3部分中一个,在当前文章中其他。在这两种方法中,我们都处理给定的字符串。在这里,在比较字符以进行扩展时,我们不得不以不同的方式处理偶数和奇数位置(因为偶数位置不表示字符串的任何字符)。

为了避免对偶数和奇数位置进行这种不同的处理,我们需要使偶数位置也代表某些字符(实际上,所有偶数位置都应代表同一字符,因为在进行字符比较时必须匹配)。这样做的一种方式是通过修改给定的字符串在所有偶数位置设置一些字符或创建给定字符串的新副本。例如,如果输入字符串为“ abcb”,则如果我们在偶数位置添加#作为唯一字符,则新字符串应为“#a#b#c#b#”。

已经讨论过的两种方法可以稍作修改,以在修改后的字符串上工作,其中不需要对偶数和奇数位置进行不同的处理。

我们也可以(在偶数和奇数位置尚未在字符串的任何地方使用)在开始和字符串末尾添加两个不同的字符作为哨兵,以避免绑定检查。通过这些更改,字符串“ abcb”将看起来像“ ^#a#b#c#b#$”,其中^和$是标记。
此实现看起来更干净,但需要更多的内存。

我们不在这里实现这些,因为它是给定实现中的简单更改。

当前文章中讨论的关于修改字符串的方法的实现可以在Longest Palindromic Substring Part II和Princeton对其进行的Java翻译中找到。