Manacher
Chivas-Regal
# 😀概念
Manacher算法又称马拉车算法,是求回文串的快速算法,其中核心思想是利用已知的来求得未知的。
# 😀思路
回文串有一个性质,就是比如"abacaba"这样一个命名为s的回文串,其中心是s[3]='c',将其设为主回文串。 左边以s[0]为中心的回文长度=右边匹配的s[6]为中心的回文长度=1 左边以s[1]为中心的回文长度=右边匹配的s[5]为中心的回文长度=3 左边以s[2]为中心的回文长度=右边匹配的s[4]为中心的回文长度=1 易得以s[s.size()-1-i]为中心的回文长度等于以s[i]为中心的回文长度 这种性质可以帮我们摆脱大量数据运行
# 😀过程
在回文串的判定时奇回文串和偶回文串的判断方式不同,我们要将原回文串两两之间与两边各插入一个字符'#'来帮我们取消这种差异(均为奇数个),同时我们要添加哨兵'@'来防止我们越0这个位置扩展导致RE 设id为主回文串的中心下标,mx为主回文串的右边界,p[i]为以i为中心的回文串半径 我们命名一个s字符串并完成插入,并初始化每个位置为中心的回文串半径为0(下面深色框代表主串中心位置)
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1.开始时使用中心扩展求得s[0]为中心的回文串半径p[0]为1,此时mx=p[0]+0=0,id=0
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 2 | 1 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 2 | 1 | 4 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 2 | 1 | 4 | 1 | 6 | 0 | 0 | 0 | 0 | 0 |
@ | # | a | # | c | # | a | # | a | # | b | # | a | # | b | # | a | # |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 2 | 1 | 4 | 1 | 2 | 3 | 2 | 1 | 4 | 1 | 6 | 1 | 4 | 1 | 2 | 3 |
我们一直担心的一种情况并未发生,但的确是可以算作一种特殊情况的,就是当本该复制的数超过了其与右边界的距离时,我们应该选择小的来定义,比如
... | i | id | i | mx | ... | 3 | 3 | 0 | 0 |
---|
# 😀Manacher函数
string Manacher(string s)
{
int len = s.size();
//-------------------------------------------插入处理(防止奇偶影响)
string ss = "$#";
rep1(i, 0, len - 1) ss += s[i], ss += "#";
//---------------------------------------------
vector<int> p(ss.size(), 0);//储存以i为中心时回文串长度
int id = 0, mx = 0;//id储存当前主回文串中心位置,mx储存当前主回文串右边界
int res_center = 0, res_len = 0;//储存最长回文串的中心点与长度
rep1(i, 0, ss.size() - 1)
{
p[i] = i < mx ? min(p[2 * id - i], mx - i) : 1;//判断i是否到达当前主回文串右边界
while(ss[i+p[i]]==ss[i-p[i]])//中心扩展更新回文串长度
++p[i];
if(mx<i+p[i])//判断是否内部回文串右边界超出主回文串右边界,超出的话改变主回文串为当前回文串
mx = i + p[i], id = i;
if(res_len<p[i])//更新哪个是最大回文串
res_len = p[i], res_center = i;
}
//返回最大回文串,若想返回长度return res_len即可
return s.substr((res_center - res_len) / 2, res_len - 1);
}
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
29
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
29