[Zuoshen Algorithm Notes] Basic Improvement - Manacher Algorithm

Question: Longest Palindromic Substring Length

For example, the length of the longest palindrome substring in 122131221 is 4

Classic solution:

Traverse each character, expand to both sides and check the longest palindrome substring

But this alone cannot find palindrome substrings of even length

So it needs to be transformed into #1#2#2#1#3#1#2#2#1#

The maximum palindrome substring length of this new string is divided by 2 and rounded to get the maximum palindrome substring length of the original string

Question extension: Does the inserted special character # need to be a character that does not appear in the string?

Answer: No need. Because the comparison is always whether the special characters are equal to the special characters, the characters in the original string are compared

Classical solution time complexity: O( N 2 N^2N2)

Manager: O(N)

Clear concept: palindrome diameter + palindrome radius

For example: #1#2#1# has a palindrome diameter of 7 and a palindrome radius of 4

Each character in a string has a corresponding palindrome diameter and palindrome radius, we need to traverse all characters to generate a palindrome radius array

In the process of generating the palindrome radius, a variable R is maintained to record the rightmost boundary of the generated palindrome substring ; a variable C is also maintained to record the center of the palindrome substring

For example: arr={#,1,#,2,#,1,#,...}

R and C are initially -1, representing the out-of-bounds position

traversal start

For arr[0]=='#', the palindrome range is 0~0, R<0, so update R to 0 and update C to 0

For arr[1]=='1', the palindrome range is 0~2, R<2, so update R to 2 and C to 1

For arr[2]=='#', the palindrome range is 2~2, R=2, so R and C are not updated

···

As long as the palindrome radius array and R, C are generated, the solution can be obtained

The manacher algorithm is a violence-based optimization of this process

Come to the i position, determine the palindrome radius and diameter according to the situation:

1. If i>R, that is, i is the first time to come to the position, then the violence will expand to both sides, looking for the palindrome radius of the substring centered on arr[i]; 0, 1, 3 in the above example This is the case for the location

2. If i<=R, it can be optimized, and it must be the following:

At this time, i' and i are symmetrical about C, and the boundaries of the palindrome substring centered on C are L and R

L      i'      C      i      R

i is on the left side of R, then C must be on the left side of i (think about the counterargument, note that C is the position that has been traversed), and the corresponding L can be found, and the symmetric point of i about C can also be found

2, 4, 5, and 6 in the above example are all like this

The following is classified according to the palindrome of i':

1) The palindrome substring centered on i' is completely within the range from L to R, then the palindrome radius of i is the same as that of i' (L to R is symmetric about C, so it is obvious), and it does not expand i got the answer directly

2) Part of the palindrome substring centered on i' is outside [L, R], for example:

(ab[cd e dcba) k abcd e dc]ft
   L   i'      C      i   R 

Then the palindrome radius of the palindrome substring centered on i is R-i+1, which will not be larger, and the answer can be obtained directly without expanding i

3) The left boundary of the palindrome substring centered on i' coincides with L

like:

[(abc d cba) k abc d cba]
      i'     C     i

Then the minimum range of the palindrome substring centered on i is already abc d cba, and it needs to be expanded violently

complete

According to the code, the time complexity can be analyzed as O(N)

    public static int manacher(String s) {
    
    
        if (s == null || s.length() == 0) return 0;
        char[] str = manacherString(s); // 1221->#1#2#2#1#
        int[] pArr = new int[str.length]; // 回文半径数组
        int R = -1, C = -1, max = Integer.MAX_VALUE; // R是回文右边界再往右一个位置,不同于笔记
        for (int i = 0; i < str.length; i++) {
    
    
            // 得到不用扩展就已经知道的以i为中心的回文区域
            // 大情况1:初始直径为1,需要往外暴力扩展
            // 小情况1:中心为i'的回文直径长度,不需要扩展
            // 小情况2:半径为R-i+1,不需要扩展
            // 小情况3:中心为i'的回文直径长度,还需要再扩展
            pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;

            // 从上述最小回文区域往外扩展
            // 为了让代码简洁,以上所有情况都扩展(不需要扩展的两种情况,第一次扩展就失败)
            // 实际上只有两种情况需要扩展
            while (i + pArr[i] < str.length && i - pArr[i] > -1) {
    
    
                if (str[i + pArr[i]] == str[i - pArr[i]]) {
    
    
                    pArr[i]++;
                } else {
    
    
                    break;
                }
            }
            if (i + pArr[i] > R) {
    
    
                R = i + pArr[i];
                C = i;
            }
            max = Math.max(max, pArr[i]);
        }
            return max - 1; // 半径减1正好是原始s的回文子串长度(不用除以2了)
    }

    public static char[] manacherString(String s) {
    
    
        char[] charArr = s.toCharArray();
        char[] res = new char[s.length() * 2 + 1];
        int index = 0;
        for (int i = 0; i != res.length; i++) {
    
    
            res[i] = (i % 2) == 0 ? '#' : charArr[index++];
        }
        return res;
    }

Palindromic radius arrays can be used for many problems

Guess you like

Origin blog.csdn.net/weixin_48288539/article/details/125032627
Recommended