【SDOI 2016】生成魔咒

传送门


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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值