持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
【模板】manacher 算法
题目描述
给出一个只由小写英文字符 组成的字符串 S ,求 S 中最长回文串的长度 。 字符串长度为 n。
输入输出格式
输入格式
一行小写英文字符 组成的字符串 S。
输出格式
一个整数表示答案。
输入输出样例
输入样例 #1
aaa
输出样例 #1
3
说明
解题思路
考虑从左向右遍历 i, 递推出 r[i].
-
记录当前已求得的回文子串右端最远延伸到的位置
maxright(maxright的左侧即为我们的已知区域 ) , 和该回文子串的回文中心 mid -
对于每个 i:
- 先判断 i 是否在
maxright的范围内, - i 在
maxright范围内, r[ ( mid << 1 ) - i ] 即为 i 关于当前 mid 的对称点处的最长回文子串回文半径长度, r[i] 就可以以 r[ (mid << 1) - i ] 为基数开始扩展.此时, 若 r[ ( mid << 1 ) - i ] 超过了 r[mid] + mid - i ( 即 i 到maxright的距离 ), 就只能将 r[i] 赋值为 r[mid] + mid - i, 因为以 i 为中心, r[ ( mid << 1 ) - i ] 为回文半径的子串右端此时已超过maxright, 即已超过已知范围, 而不能确定 r[i] 实际上能否达到该长度.故 r[i] = min( r[ ( mid << 1 ) - i ], r[mid] + mid - i ); 并进行 '4.'. - i 超出
maxright, 不能利用对称性, 故 r[i] = 1 - 以当前 i 和 r[i] 开始暴力向外拓展至求得 r[i] 最终结果. 并将其右端与
maxright比较, 更新maxright.
- 先判断 i 是否在
#include<bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int n = s.size();
string t(n * 2 + 1, ' ');
int idx = 0;
for(int i = 0; i < n * 2 + 1; i++) t[i] = (i & 1) == 0 ? '#' : s[idx++];
vector<int> p(n * 2 + 1);
int C = -1, R = -1, ret = -1e9;
for(int i = 0; i < n * 2 + 1; i++) {
p[i] = R > i ? min(p[2 * C - i], R - i) : 1;
while(i + p[i] < n * 2 + 1 and i - p[i] > -1) {
if(t[i + p[i]] == t[i - p[i]]) p[i]++;
else break;
}
if(i + p[i] > R) {
R = i + p[i];
C = i;
}
ret = max(ret, p[i]);
}
cout << ret - 1 << endl;
return 0;
}