Mancher算法详解附带练习题

Mancher算法

1. 介绍

  • 用来计算回文子串,用一个数组记录每个位置上,以当前位置为中心,往两边扩的最长回文半径

2. 求回文子串长度

有两种方法:

方法一:

  • 暴力求解,无优化,以每个位置为中心,往两侧匹配。
  • 这个方法有一个缺陷,只能匹配奇数的时候,比如aba,如果是abba中间的两个b就匹配不上。
  • 解决方法是将每个字符中间都加上一个特殊字符#,任意字符都行,就算是字符串中存在的字符也是可以的
  • 然后字符串abba就会变成#a#b#b#a#,这时候再往两侧扩就不会出现错误

方法二:

  • Mancher算法求长度
  • 首先将原字符串加上特殊字符,变成Mancher串
  • 由变量C,R 和一个数组arr 进行求最长回文子串长度
  • 变量C的含义:最长回文子串的中心位置
  • 变量R的含义:最右匹配成功的位置 + 1
  • arr数组的含义:当前位置的回文长度
  • for循环从i = 0遍历到字符串结尾有四种情况:
    • 了解4个case之前,需要了解一个关键点
    • 因为C是中心位置,C所在的位置左右两侧有一个很大的回文区域left...right范围,然后i位置是在C位置之后的,所以i位置可以跟 C位置作一个对称,得到i',也就是这样子i'...C...i
    • case1:i > R代表iR外,只能暴力求解
    • case2:i < R表示iR内,此时i根据C找到对应i'的位置,直接取arr[i']的值,因为在C这个大范围内,C的左侧跟C的右侧都是回文,因为i'i的对称,所以arr[i']的值和arr[i]的值也一样。
    • case3:当i < R,此时i根据C找到对应i'的位置,如果i'的范围已经不在C的回文范围内了,这时候就不能直接取arr[i']的值了,因为i在C的回文范围内,所以i的最长回文长度只能是C回文范围的右侧,也就是R,所以arr[i] = R - i
    • case4:当i < R,此时i根据C找到对应i'的位置,如果i'的左侧压到了C回文范围的左侧,这时候就不能直接取arr[i']的值了,但是至少可以取一个已经确定的回文长度,也就是C的右侧回文范围,也就是R的位置,所以arr[i]的回文长度可以从R-i开始计算,如果后面遇到了回文,长度就继续增加

3. 完整代码

#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. 关于Mancher的相关题目

对于一个字符串,我们想通过添加字符的方式使得新的字符串整体变成回文串,但是只能在原串的结尾添加字符,请返回在结尾添加的最短字符串。

给定原字符串A及它的长度n,请返回添加的字符串。保证原串不是回文串。

测试样例:
“ab”,2
返回:“a”


题目是核心代码模式,我这边写成了ACM模式

思路:
Mancher算法中的arr数组中每个位置的含义:以当前位置为中心,向两侧扩,最长回文子串的长度
所以这题用Mancher来解,题意是在字符串末尾加字符,所以说只需要在求arr数组的时候,遇到了一个位置的右侧可以扩到字符串末尾,把长度保存下来,然后退出循环遍历
在原字符串中求0位置原字符串的长度减去刚刚保存的长度加1的子串,然后将子串逆序,就是答案了

代码

#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;
}

推荐文章

KMP算法详解附带练习题

猜你喜欢

转载自blog.csdn.net/weixin_44839362/article/details/117135503