Manacher学习笔记
# Manacher算法
manacher算法是一个可以在复杂度内求出一个字符串所有回文子串的算法。
# 算法步骤
给定一个长度为的字符串,求其所有回文子串
# 预处理
向中插入个分隔符(所选的分隔符不能在中出现过),得到串
例如,若,分隔符为,则有
这样一来就避免了讨论回文子串的奇偶性
# 计算回文子串
定义数组为串中,以号字符为中心的回文子串的半径。
例如,,则
显然以为中心的回文子串的长度等于
在计算的过程中,我们维护找到的最右(即右边界最大)回文子串。
假设都已经计算完毕,现在求。
下面分情况讨论
若,则我们通过朴素算法,一位一位计算
否则,有:
由于串是回文串,令,有如下情况
其中回文子串1等于回文子串2
但由于的位置我们尚未扫描,不知道是否能构成回文子串,因此需要抛弃
因此有
接着继续通过朴素算法扫描即可
# 模板(洛谷P3805)
题意:给定字符串,求其最长回文子串的长度
1.47s
char str[maxn], s[2 * maxn];
int d[2 * maxn];
void solve(){
cin >> str;
int len = strlen(str);
s[0] = '#';
for(int i=0; i<len; i++){
s[(i << 1) + 1] = str[i];
s[(i << 1) + 2] = '#';
}
len = (len << 1) + 1;
s[len] = 0;
int ans = 0;
for(int i=1, mid = 0, r = -1; i<len; i++){
d[i] = (i <= r) ? min(r - i + 1, d[(mid << 1) - i]) : 1;
while(d[i] <= i && i + d[i] < len && s[i - d[i]] == s[i + d[i]]) d[i]++;
if(i + d[i] - 1 > r) {
r = i + d[i] - 1;
mid = i;
}
ans = max(ans, d[i] - 1);
}
cout << ans << '\n';
}
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
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
如果在字符串前再插入一个其他字符(即非分隔符,亦未在中出现过),则可以减少while
中的条件判断(会快一点点)
1.25s
void solve(){
cin >> str;
int len = strlen(str);
s[0] = '$', s[1] = '#';
for(int i=0; i<len; i++){
s[(i << 1) + 2] = str[i];
s[(i << 1) + 3] = '#';
}
len = (len << 1) + 2;
s[len] = 0;
int ans = 0;
for(int i=1, mid = 0, r = -1; i<len; i++){
d[i] = (i <= r) ? min(r - i + 1, d[(mid << 1) - i]) : 1;
while(s[i - d[i]] == s[i + d[i]]) d[i]++;
if(i + d[i] - 1 > r) {
r = i + d[i] - 1;
mid = i;
}
ans = max(ans, d[i] - 1);
}
cout << ans << '\n';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 参考链接
https://oi-wiki.org/string/manacher/
https://www.luogu.com.cn/problem/solution/P3805
上次更新: 2021/02/24, 03:37:30