可以快速得到有关 回文子串 的相关信息 . 板子题
本质上就是对 暴力的优化 和 现有信息的利用 .
暴力寻找回文子串, 无非就是枚举 对称轴, 往两边扩展, 时间复杂度 O(N2) .
而 manacher 则是在暴力的基础上, 利用了前面已经求出的回文子串信息,
具体地说:
设 变量 Max_r 表示当前回文串所能触及的 最右端点, mid 表示那个触及 Max_r 的回文串 的 中点,
且当前到了 i 位置, 要以 i 为对称轴扩展出所有 连续 回文子串, j 为 i 关于 mid 的 对称点,
hw[i] 表示 i 位置的最长回文串 长度的一半 .
- 若 i 在 Max_r 代表的回文串内, 则 j 为对称轴的回文串 在 Max_r 的 “领地” 里的回文长度可以完全 继承 给 i 位置 .
- 否则只能暴力扩张 i 位置 .
因为 Max_r 只扩展到 N, 所以 时间复杂度 O(N).
很多情况对称轴为 间隙 也要考虑, 此时可以在字符之间插入 特殊字符 后正常 跑 Manacher.
具体可以看代码 .
#include<bits/stdc++.h>
const int maxn = 21000005;
int Ans;
int hw[maxn<<1];
void Manacher(char *s){
int max_r = 0, mid = 0;
int len = strlen(s+1);
for(int i = 1; i <= len; i ++){
if(i < max_r) hw[i] = std::min(hw[(mid<<1) - i], max_r-i+1);
while(i + hw[i] <= len && i - hw[i] >= 1 && s[i + hw[i]] == s[i - hw[i]]) hw[i] ++;
if(i + hw[i] - 1 > max_r) max_r = i + hw[i] - 1, mid = i;
Ans = std::max(Ans, hw[i]-1);
}
}
char s[maxn], s1[maxn<<1];
int main(){
scanf("%s", s + 1);
int len = strlen(s+1);
for(int i = 1; i <= len<<1; i ++){
if(i & 1) s1[i] = '#';
else s1[i] = s[i>>1];
}
s1[(len<<1)+1] = '#';
Manacher(s1);
printf("%d\n", Ans);
return 0;
}
P1659[国家集训队]拉拉队排练
P4555[国家集训队]最长双回文串
P4199万径人踪灭