【传送门:BZOJ4516】
简要题意:
给出一个长度为n的数字字符串,求出每个数字插入到字符串结尾时的不同子串个数
题解:
后缀自动机
对于一个状态s,他的right集合代表的子串的长度就是(dep[fail],dep[s]]。这道题我们需要动态的维护不同子串的个数,每次从头扫一遍直接计算肯定不行,我们考虑加入一个新字符会产生多少新的不同子串,这个个数其实就是(dep[fail],dep[s]]的区间长度(自行yy吧)
注意加long long,而且字符种数太多了,用map存
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<map> using namespace std; typedef long long LL; map<int,int>tr[210000]; int dep[210000],fail[210000],cnt,last,root; int a[110000]; void add(int k) { int x=a[k]; int np=++cnt,p=last; dep[np]=k; while(p!=0&&tr[p][x]==0) tr[p][x]=np,p=fail[p]; if(p==0) fail[np]=root; else { int q=tr[p][x]; if(dep[q]==dep[p]+1) fail[np]=q; else { int nq=++cnt; tr[nq]=tr[q]; dep[nq]=dep[p]+1; fail[nq]=fail[q]; fail[q]=fail[np]=nq; while(p!=0&&tr[p][x]==q) tr[p][x]=nq,p=fail[p]; } } last=np; } int Rsort[110000],sa[210000],r[210000],sum[210000]; int main() { int n; scanf("%d",&n); cnt=last=root=1; LL ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); add(i); ans+=dep[last]-dep[fail[last]]; printf("%lld ",ans); } return 0; }