抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

ProPro

定义一个串AAQQ的周期,仅当AAQQproperproper前缀(即A!=QA!=Q的意思),而且QQAAAA的前缀(没有properproper的限制)。比如说abababababababababababababaabababa的周期。

给你一个字符串,求出它所有前缀的最大周期长度之和。

SolSol

首先先把A,QA,Q画出来。

发现绿色部分是相等的,而且绿色部分是AA的真borderborder(既是AA的前缀又是AA的后缀而且不等于AA)。

发现周期长度为len(Q)len(border)len(Q)-len(border),于是想让周期尽量长,就要borderborder尽量短。

问题转化为求AA的每个前缀的最短非空borderborder

这就提示我们建立failfail树,何谓failfail树,就是iinext[i]next[i]连边形成的树形结构。

发现ii到根节点的路径上的节点代表A[1i]A[1 \to i]的所有borderborder

具体证明参见这篇博客

建出failfail树之后,发现对于和00相邻的所有节点,它的子树里面的所有节点的最短非空borderborder的长度都为这个节点的标号,很好理解。

于是可以通过一个类似于染色的方法,O(n)O(n)地计算出来对于每个节点的最短非空borderborder的长度。

最后统计答案扫一遍即可,记得开long long。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
#define MAXN 1000005
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
char a[MAXN];
int nex[MAXN];
vector<int>G[MAXN];
int num[MAXN];
inline void dfs(int u,int father,int color){
num[u]=color;
for (register int i=0;i<G[u].size();++i){
if (color==0) dfs(G[u][i],u,G[u][i]);
else dfs(G[u][i],u,color);
}
}
int main(){
int n=read();
scanf("%s",a+1);
int j=0;
for (register int i=2;i<=n;++i){
while (j&&a[j+1]!=a[i]) j=nex[j];
if (a[j+1]==a[i]) ++j;
nex[i]=j;
}
for (register int i=1;i<=n;++i){
G[nex[i]].push_back(i);
}
dfs(0,0,0);
long long ans=0;
for (register int i=1;i<=n;++i){
ans+=i-num[i];
}
printf("%lld\n",ans);
}

评论