Manacher部分:
下标 i :0 是$ , 原字符串插入#字符变为 奇数长度,结尾位置添加@ 维持奇数字符个数
arr字符串: 经过处理的字符串 , eg -> fabbac “$ # f # a # b # b # a # c # @”
辅助数组p:p[i] 表示 arr字符串 在 i 位置的最长回文半径
两个关系:
- 最长回文串(是原串"fabbac"的最长回文串长度) = p[i] - 1;
- 以 i 为中心的 回文串( arr串 ) 起始位置(索引) = ( i - p[i] ) / 2;
知道这些,再看代码就能秒懂了。
板子:
#include
#include
#include
#include
#include
#include
#include
回文树部分
套用AC自动机思想,建立两颗树
0树 和 1树 , 前者表示偶数回文串,后者表示奇数回文串
通过fail指针操作可以在两棵树中 来回跳跃
具体内容 就看代码解释,可以很好地理解
主要是要知道
- fail 指向当前now位置 前的最长回文后缀 的 回文后缀
- last在运行过程中会一直改变,所以 S[ 1 ~ i ] 的本质不同的回文串 是 cnt - 1 个
还有一些点需要了解
我们计算给出的S串的 所有回文子串(包括重复的)的数目,有两种方法
- 第一种是用count函数,但是计算的时候0树 和1树 的cnt被重复计算了,所以只算一个就行。
void count() {
All = 0;//注意取模问题
for(int i = cnt ; i >= 0 ; --i){
rcnt[fail[i]] = (rcnt[fail[i]] + rcnt[i]) % Mod;
if(i > 0) All = (All + rcnt[i]) % Mod;
}
}
- 在for(1 ~ lens)的过程中 把每次的 num[last] 加到一个sum中即可。思考一下,这个方法不仅是计算所有回文子串数目的方法,也是计算 已插入字符集(可以是1 ~ i 或 n ~ i) 的回文子串数目——包括重复部分(再强调一下)。
//sum[lens] 就是全部回文子串的数目
sum[0] = 0;
for(int i = 1 ; i <= lens ; ++i){
add(S[i]);
sum[i] = (sum[i-1] + (LL)num[last]) % Mod;
}
//_________________________________
//sum[1]也是全部回文子串的数目
sum[lens+1] = 0;
for(int i = lens ; i >= 1 ; --i){
add(S[i]);
sum[i] = (sum[i+1] + (LL)num[last]) % Mod;
}
当这个模板处理字符串过长时可能会MLE,这时候就需要一些处理,或者直接考虑放弃此算法,改用manacher了,因为我们改用链表模拟时,如果要处理的字符串中字符范围是100,很可能会TLE,当然只有大小写字母是能满足的。
用下面第一个模板1 在2e6 * 26时就会MLE , 改用模板2 可过,当然速度稍慢一些,毕竟常数大了点。
模板1(数组模拟版)
const int Maxn = 5e5 + 7;
const int Inf = 1e9 + 7;
const int Over = 26;
int N , M;
struct Plt{
char str[Maxn];
int lens;
int nxt[Maxn][Over];
int fail[Maxn];
int cnt[Maxn];
int num[Maxn];
int len[Maxn];
int S[Maxn];
int last;
int n;
int p;
int newnode(int l) {
for (int i = 0 ; i < Over ; ++ i) nxt[p][i] = 0;
cnt[p] = 0;
num[p] = 0;
len[p] = l;
return p++;
}
void init() {
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
S[n] = -1;
fail[0] = 1;
}
int get_fail(int x) {
while(S[n - len[x] - 1] != S[n]) x = fail[x];
return x;
}
void add(int c) {
c -= 'a';
S[++n] = c;
int cur = get_fail(last);
if (!nxt[cur][c]) {
int now = newnode(len[cur] + 2);
fail[now] = nxt[get_fail(fail[cur])][c];
nxt[cur][c] = now;
num[now] = num[fail[now]] + 1;
}
last = nxt[cur][c];
cnt[last]++;
}
void count() {
for (int i = p - 1 ; i > 0 ; --i){
cnt[fail[i]] += cnt[i];
}
}
void Buildtree(){
for(int i = 0 ; i < lens ; ++i){
add(str[i]);
}
}
void solve(){
lens = strlen(str);
init();
Buildtree();
count();
}
};
模板2(链表版)
#include
#include
#include
#include
#include
#include
#include
题目补充:
洛谷 4555
洛谷1659
(下面的https://vjudge.net/contest/321569 密码:rushb)
HYSBZ - 3676
URAL - 1960
UVALive - 7041
CodeForces - 17E
HDU - 3948
题解:
https://blog.csdn.net/castomere/article/details/100081064