题目:BZOJ4516.
题目大意:初始一个字符串为空,现在要求每次加入一个字符编号
x
x
x并输出这个字符串目前本质不同的子串数量.
加入操作次数
≤
1
0
5
\leq 10^5
≤105,
1
≤
x
≤
1
0
9
1\leq x\leq 10^9
1≤x≤109.
求一个串本质不同的子串数量是SAM的经典应用,直接统计所有节点的 m a x − m i n + 1 max-min+1 max−min+1之和即可.
但是这样子是可以一次性统计的做法,要求支持加入字符呢?其实很容易想到加入一个字符就相当于创建了一个新点,直接加上新点的贡献即可.有可能要分裂出来的点其实不需要去管它,因为分裂后它们的贡献与分裂前是一样的.
同时由于字符集太大,用map维护即可.
时间复杂度 O ( n log n ) O(n\log n) O(nlogn).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,C=26;
struct automaton{
map<int,int>s;
int len,par;
}tr[N*2+9];
int cn,last;
LL ans=0;
void Build_sam(){cn=last=1;}
void extend(int x){
int p=last,np=++cn;
tr[np].len=tr[p].len+1;
last=np;
while (p&&!tr[p].s[x]) tr[p].s[x]=np,p=tr[p].par;
if (!p) tr[np].par=1;
else {
int q=tr[p].s[x];
if (tr[p].len+1==tr[q].len) tr[np].par=q;
else {
tr[++cn]=tr[q];tr[cn].len=tr[p].len+1;
tr[q].par=tr[np].par=cn;
while (p&&tr[p].s[x]==q) tr[p].s[x]=cn,p=tr[p].par;
}
}
ans+=(LL)tr[np].len-tr[tr[np].par].len;
}
Abigail getans(){
int n,x;
scanf("%d",&n);
Build_sam();
for (int i=1;i<=n;++i){
scanf("%d",&x);
extend(x);
printf("%lld\n",ans);
}
}
int main(){
getans();
return 0;
}