串串全家桶
Manacher
用途:
该算法可在 \(O(n)\) 的时间复杂度下求出以每一个点(和缝,即回文串长为偶数的情况)为中心的最长回文串长度。
做法:
前置:定义朴素算法为,对于每一个点为中心的情况,暴力枚举是否可以扩展两边,直到不能扩展,此时得到结果。
代码如下,其中 \(a\) 是字符数组, \(x\) 是我们计算的中心,而 \(ans_x\) 则是答案。
while(a[x-ans[x]-1]==a[x+ans[x]+1])++ans[x];
- 预处理:在原串的 \(n+1\) 个缝里插入任意不在原串中的字符,在首和尾再插入两种与之前不同的字符
举个例子,若原串是abcdefg,一种合法的预处理方式是@#a#b#c#d#e#f#g#$。插入的三种字符互不相同。
在这个例子中,插入#的原因是显然的,如果我们有a#a,我们就可以计算出原串中长度为 \(2\) 的回文串。
插入@$的原因是将他们作为哨兵,他们不可能被包含在回文串中,所以扫到它们的时候我们可以不用特判而知道停止扫描。 - 处理 \(ans\) :假设我们在处理位置 \(i\) 前已经处理出了 \(ans_{1\sim i-1}\) ,我们维护当前最右的被之前的回文串包住的位置为 \(r\),则若 \(r<=i\),则我们直接对该位置做朴素算法;否则,我们可以给 \(ans_i\) 赋上一个初值,从而减少重复的枚举。
若使得 \(r\) 最大的位置为 \(p\) ,则我们可以把 \(i\) 关于 \(p\) 对称的位置的 \(ans\) 赋给 \(i\),给出一张图来解释原因。

但是,我们不难发现,这个好像只在 \(p\) 的回文串包含 \(i'\) 时才成立,如果是下图的情况,我们简单分析可以发现初值应为 \(r-i\) ,因为我们并不能保证两绿色段拼上后合法。

由此我们做完了。
复杂度分析
$case_1: i\ge r $ 此时,朴素算法拓展次数等于 \(r\) 的增量。
$case_2: i<r \wedge $ 回文串范围被 \(r\) 覆盖 此时 \(ans_i\) 初值等于末值,朴素算法执行 \(1\) 次。
$case_3: i<r \wedge $ 回文串范围未被 \(r\) 覆盖 此时 \(i+ans_i\) 初始 \(=r\) ,朴素算法拓展次数等于 \(r\) 的增量。
由于 \(r\) 最多被增加 \(n\) 次,所以该算法复杂度为 \(O(n)\) 。
code
int r,nid;
void calc(int x)
{
while(a[x-ans[x]-1]==a[x+ans[x]+1])++ans[x];
if(r<x+ans[x])
{
r=x+ans[x];
nid=x;
}
}
for(int i=1;i<=tp;i++)
{
if(i<=r)
{
ans[i]=min(ans[nid*2-i],r-i);
}
calc(i);
}
AC自动机
trie套kmp,然后fail树上差分
为什么要树上差分????
这样,你先匹配一下:原串 abba ,模式串 abba , bb ,你就会从1号点一直跳到5号点,然后发现, \(fail_5\) 跳到的是 \(2\) ,下面的 bb 匹配不上。

注意:求fail要bfs而非dfs,,,卡了好久
code
#include<bits/stdc++.h>
// #define DEBUG
#ifdef DEBUG
#define perr(...) fprintf(stderr,__VA_ARGS__)
#else
#define perr(...) void(0)
#endif
using namespace std;
const int mn=2e5+5,ms=2e6+5;
int n;
int len[mn],cnt,lb;
char a[ms],*s[mn];
int ed[mn];
char b[ms];
int hd[ms],to[ms<<1],nxt[ms<<1];
int cf[ms];
queue<int> q;
void add(int x,int y)
{
nxt[cnt]=hd[x];
to[cnt]=y;
hd[x]=cnt++;
}
struct ACa
{
struct node
{
int a[26];
int fail;
void clear()
{
fail=1;
for(int i=0;i<26;i++)
{
a[i]=0;
}
}
}a[ms];
int tp=1;
void bld(int x)
{
int p=1;
for(int i=1;i<=len[x];i++)
{
int t=s[x][i]-'a';
if(a[p].a[t]==0)
{
a[p].a[t]=++tp;
a[tp].clear();
}
p=a[p].a[t];
}
ed[x]=p;
}
void asdf()
{
q.push(1);
while(!q.empty())
{
int x=q.front();
q.pop();
if(x!=1)
{
add(a[x].fail,x);
}
assert(x>=0);
for(int i=0;i<26;i++)
{
if(a[x].a[i]!=0)
{
if(x==1)
{
a[a[x].a[i]].fail=1;
}
else
{
int t=a[x].fail;
while(t!=1 && a[t].a[i]==0)
{
t=a[t].fail;
}
if(a[t].a[i])t=a[t].a[i];
a[a[x].a[i]].fail=t;
}
q.push(a[x].a[i]);
}
}
}
}
void init()
{
int p=1;
for(int i=1;i<=lb;i++)
{
int t=b[i]-'a';
while(p!=1 && a[p].a[t]==0)
{
p=a[p].fail;
// cf[p]++;
}
if(a[p].a[t]!=0)
{
p=a[p].a[t];
}
cf[p]++;
perr("p%d\n",p);
}
}
void dfs(int x)
{
for(int i=hd[x];i;i=nxt[i])
{
int u=to[i];
dfs(u);
cf[x]+=cf[u];
}
}
}ac;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
s[i]=a+cnt;
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
cnt+=len[i]+5;
perr("%s %d\n",s[i]+1,len[i]);
}
scanf("%s",b+1);
lb=strlen(b+1);
ac.a[1].clear();
cnt=2;
perr("hi");
for(int i=1;i<=n;i++)
{
ac.bld(i);
}
perr("hi");
ac.asdf();
perr("hi");
ac.init();
ac.dfs(1);
for(int i=1;i<=n;i++)
{
printf("%d\n",cf[ed[i]]);
}
}

浙公网安备 33010602011771号