Problem
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1 1 1、 2 2 2 拼凑起来形成一个魔咒串 [ 1 , 2 ] [1,2] [1,2]。
一个魔咒串 S S S 的非空字串被称为魔咒串 S S S 的生成魔咒。
例如 S = [ 1 , 2 , 1 ] S=[1,2,1] S=[1,2,1] 时,它的生成魔咒有 [ 1 ] [1] [1]、 [ 2 ] [2] [2]、 [ 1 , 2 ] [1,2] [1,2]、 [ 2 , 1 ] [2,1] [2,1]、 [ 1 , 2 , 1 ] [1,2,1] [1,2,1] 五种。 S = [ 1 , 1 , 1 ] S=[1,1,1] S=[1,1,1] 时,它的生成魔咒有 [ 1 ] [1] [1]、 [ 1 , 1 ] [1,1] [1,1]、 [ 1 , 1 , 1 ] [1,1,1] [1,1,1] 三种。
最初 S S S 为空串。共进行 n n n 次操作,每次操作是在 S S S 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 S S S 共有多少种生成魔咒。
Solution
后缀自动机的模板题。
加进来一个字符后,新产生的子串数其实就是 c u r cur cur 的终点等价类集合大小。
然后一边加一遍更新答案即可。
注:这道题的字符集大小比较大,要用离散化或 m a p map map 处理。
Code
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
int n,x,last,Size;
ll ans;
struct SAM{
int len,link;
map<int,int>nxt;
}a[N<<1];
void Insert(int c){
int p,cur=++Size;
a[cur].len=a[last].len+1;
for(p=last;p!=-1&&!a[p].nxt.count(c);p=a[p].link) a[p].nxt[c]=cur;
if(p==-1) a[cur].link=0;
else{
int now=a[p].nxt[c];
if(a[now].len==a[p].len+1)
a[cur].link=now;
else{
int Clone=++Size;
a[Clone]=a[now],a[Clone].len=a[p].len+1;
for(;p!=-1&&a[p].nxt[c]==now;p=a[p].link)
a[p].nxt[c]=Clone;
a[cur].link=a[now].link=Clone;
}
}
ans+=a[cur].len-a[a[cur].link].len,last=cur;
}
int main(){
scanf("%d",&n);
a[0].link=-1,ans=0;
for(int i=1;i<=n;++i){
scanf("%d",&x),Insert(x);
printf("%lld\n",ans);
}
return 0;
}