Detailed explanation of Mancher algorithm with practice questions

Mancher algorithm

1 Introduction

  • It is used to calculate the palindrome substring, and an array is used to record the longest palindrome radius expanded to both sides with the current position as the center

2. Find the length of the palindrome substring

There are two methods:

method one:

  • Brute force solution without optimization, with each position as the center and matching on both sides.
  • This method has a flaw, it can only match odd numbers, for example aba, if it is abbathe two b in the middle, it will not match.
  • The solution is to add a special character in the middle of each character #, any character is fine, even the characters that exist in the string are also possible
  • Then the string abbawill become #a#b#b#a#, at this time, if you expand to both sides, there will be no error

Method Two:

  • Mancher algorithm to find the length
  • First add special characters to the original string to become a Mancher string
  • Use variables C, R and an array arr to find the length of the longest palindrome substring
  • The meaning of variable C: the center position of the longest palindrome substring
  • The meaning of the variable R: the position where the rightmost match succeeds + 1
  • The meaning of the arr array: the length of the palindrome at the current position
  • i = 0There are four situations in the for loop from traversal to the end of the string:
    • Before understanding the 4 cases, you need to understand a key point
    • Because C is the center position, there is a large palindrome area on the left and right sides of the position where C is located left...right, and ithe position is after the position of C, so ithe position can be symmetrical with the position of C, and it is obtained i', that is, like thisi'...C...i
    • case1: i > Rthe representative iis outR , and can only be solved violently
    • case2: i < RIndicates ithatR , at this time, find the corresponding position iaccording to C , and take the value directly, because in the large range of C, the left side of C and the right side of C are palindrome, because they are symmetrical, so the value The same goes for the value of and .i'arr[i']i'iarr[i']arr[i]
    • case3: When i < R, at this time , the icorresponding position is found according to C. i'If i'the range of is no longer within the range of the palindrome of C, arr[i']the value of the value cannot be directly taken at this time, because it iis within the range of the palindrome of C, so ithe longest reply The text length can only be the right side of the C palindrome range, which is R, soarr[i] = R - i
    • case4: When i < R, at this time, ifind the corresponding position according to C i', if i'the left side of C is pressed to the left of the palindrome range of C, then arr[i']the value cannot be directly taken, but at least a determined palindrome length can be taken, and It is the palindrome range on the right side of C, that is, the position of R, so arr[i]the palindrome length can R-ibe calculated from the beginning. If a palindrome is encountered later, the length will continue to increase

3. Complete code

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

string mancherString(string s) {
    
    
	string str = "#";
	for (int i = 0; i <= s.size(); i++) {
    
    
		str.push_back(s[i]);
		str.push_back('#');
	}
	return str;
}

int main() {
    
    
	string s = "abc1234321ab";
	string str = mancherString(s); // #a#b#c#1#2#3#4#3#2#1#a#b#
	vector<int> pArr(str.size(), 0);
	int C = -1; // 回文串的中心位置
	int R = -1; // 最右的扩成功位置,再下一个位置
	int Max = -1;
	for (int i = 0; i < str.size(); i++) {
    
    
		// i'...C...i
		// 当前位置是i  可以根据C对称的找到i' , 因为C所在的位置左右两侧有一个很大的回文区域left...right范围
		// case1: i'回文串的范围 在C回文串范围left和right内
		// 此时i可以直接取i'的值,因为i也在C的范围内,然后又是对称取的i'

		// case2: i'回文串的范围 在C回文范围left和right外
		// 因为i'的范围已经不在left和right内了,但是i在C的回文范围内,i最大长度也只能到C的right,也就是R
		// 此时直接出答案 i的值为R-i

		// case3: i'回文串的范围 左侧压到了C回文范围的left
		// 也就是说,i的最小长度是可以到C的right,当初C的left-1和right+1不是回文,只是因为这两个值不同
		// 但是有可能i的left-1位置和C的right+1位置相同,所以说得从right+1开始判断是否回文
		pArr[i] = R > i ? min(pArr[C * 2 - i], R - i) : 1; // C * 2 - i 就是 i'的位置
		
		// i - pArr[i]相当于left , i + pArr[i]相当于right
		// i - pArr[i] 左边最开始待匹配的位置 , i + pArr[i] 右边最开始待匹配的位置
		while (i - pArr[i] > -1 && i + pArr[i] < str.size()) {
    
    
			// 暴力扩,如果右侧第一个位置跟左侧第一个位置相等,代表回文长度需要加1,因为又是一个回文
			if (str[i - pArr[i]] == str[i + pArr[i]]) {
    
    
				pArr[i]++;
			}
			else {
    
    
				// 如果中了case1和case2会直接退出循环
				break;
			}
		}
		// 更新C和R
		if (i + pArr[i] > R) {
    
    
			R = i + pArr[i];
			C = i;
		}
		// 记录最大长度
		Max = max(Max, pArr[i]);
	}
	cout << "Ans :" << Max - 1 << endl; // Ans :7
	return 0;
}

4. Related topics about Mancher

For a string, we want to make the new string a palindrome as a whole by adding characters, but only characters can be added at the end of the original string, please return the shortest string added at the end.

Given the original string A and its length n, please return the added string. Make sure the original string is not a palindrome.

Test example:
"ab", 2
returns: "a"


The title is the core code mode, and I wrote it as the ACM mode

Idea:
The meaning of each position in the arr array in the Mancher algorithm: take the current position as the center, expand to both sides, and the length of the longest palindrome substring So
this question is solved by Mancher, and the meaning of the question is to add characters at the end of the string , so you only need to find the right side of a position that can be extended to the end of the string when finding the arr array, save the length, and then exit the loop to traverse the substring obtained in the original string, and then reverse the order of the
substring , is the answer0位置原字符串的长度减去刚刚保存的长度加1

the code

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

string mancherString(string s) {
    
    
	string str = "#";
	for (int i = 0; i < s.size(); i++) {
    
    
		str.push_back(s[i]);
		str.push_back('#');
	}
	return str;
}

int process(string str) {
    
    
	int C = -1;
	int R = -1;
	int ans = 0;
	vector<int> pArr(str.size());
	for (int i = 0; i != str.size(); i++) {
    
    
		pArr[i] = R > i ? min(pArr[C * 2 - i], R - i) : 1;
		while (i + pArr[i] < str.size() && i - pArr[i] > -1) {
    
    
			if (str[i + pArr[i]] == str[i - pArr[i]]) {
    
    
				pArr[i]++;
			}
			else {
    
    
				break;
			}
		}
		if (i + pArr[i] > R) {
    
    
			C = i;
			R = i + pArr[i];
		}
		if (R == str.size()) {
    
    
			ans = pArr[i];
			break;
		}
	}
	return ans;
}

int main() {
    
    
	string s = "abbbbbaa"; 
	string str = mancherString(s);
	int len = process(str);
	cout << len;
	string ans = s.substr(0, s.size() - len + 1);
	reverse(ans.begin(), ans.end());
	cout << ans;//bbbbba
	return 0;
}

recommended article

Detailed explanation of KMP algorithm with practice questions

Guess you like

Origin blog.csdn.net/weixin_44839362/article/details/117135503