Submit: 947 Solved: 529
Description
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、
[1,1]、[1,1,1] 三种。最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都
需要求出,当前的魔咒串 S 共有多少种生成魔咒。
Input
第一行一个整数 n。
第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。
1≤n≤100000。,用来表示魔咒字符的数字 x 满足 1≤x≤10^9
Output
输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量
Sample Input
7
1 2 3 3 3 1 2
1 2 3 3 3 1 2
Sample Output
1
3
6
9
12
17
22
3
6
9
12
17
22
HINT
Source
字符串 后缀自动机
刷刷水题233
每添加一个字符,ans+=L[np]-L[fa[np]]
想一想,为什么?(逃)
后缀自动机上一个结点存的可能是多个后缀(也就是多个串),根据SAM的基本概念可以知道,这些串的长度互不相同且连续变化,记其中最长的长度为max,最短的长度为min,那么就有max-min+1个不同的串。
又根据SAM的性质可以知道min[x]=max[fa[x]]+1,所以这个结点有$ max[x]-(max[fa[x]]+1) +1 $个不同的串。
后缀自动机上不同的结点存的串本质不同,所以可以如上累加。
1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 #include<map> 9 #define LL long long 10 using namespace std; 11 const int mxn=201010; 12 int read(){ 13 int x=0,f=1;char ch=getchar(); 14 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 15 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 void write(LL x){ 19 if(x<0)putchar('-'),x=-x; 20 if(x>9)write(x/10); 21 putchar(x%10+'0'); 22 return; 23 } 24 struct SAM{ 25 map<int,int>t[mxn]; 26 int fa[mxn],l[mxn]; 27 int S,last,cnt; 28 LL ans; 29 void init(){ 30 S=last=cnt=1;ans=0; 31 return; 32 } 33 void add(int c){ 34 int p=last,np=++cnt;last=np; 35 l[np]=l[p]+1; 36 for(;p && !t[p][c];p=fa[p])t[p][c]=np; 37 if(!p){fa[np]=S;} 38 else{ 39 int q=t[p][c]; 40 if(l[q]==l[p]+1){ 41 fa[np]=q; 42 } 43 else{ 44 int nq=++cnt; 45 l[nq]=l[p]+1; 46 t[nq]=t[q]; 47 fa[nq]=fa[q]; 48 fa[q]=fa[np]=nq; 49 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq; 50 } 51 } 52 ans+=l[np]-l[fa[np]]; 53 return; 54 } 55 }sa; 56 int n,x; 57 int main(){ 58 int i,j; 59 n=read(); 60 sa.init(); 61 for(i=1;i<=n;i++){ 62 x=read(); 63 sa.add(x); 64 write(sa.ans);puts(""); 65 } 66 return 0; 67 }