【题解】P4070 [SDOI2016]生成魔咒(SAM/后缀自动机)

原题链接

[SDOI2016]生成魔咒

题目描述

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 共有多少种生成魔咒。

输入格式

第一行一个整数 n

第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符 xi

输出格式

输出 n 行,每行一个数。
i 行的数表示第 i 次操作后 S 的生成魔咒数量

样例 #1

样例输入 #1

7
1 2 3 3 3 1 2

样例输出 #1

1
3
6
9
12
17
22

提示

数据规模与约定

对于 10% 的数据,保证 1n10
对于 30% 的数据,保证 1n100
对于 60% 的数据,保证 1n103
对于 100% 的数据,保证 1n1051xi109

题解

考虑SAM中的link指向的是和它endpos不同的最长后缀,新加点的长度减去它的link的长度即增加的长度,直接建SAM即可。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}

const int N=100001;
struct node{
	unordered_map<int,int>to;
	int len,fro;
}sam[N*2];
int n,last,cnt;
long long ans;

void insert(int c){
	int p=last,now=++cnt;
	sam[now].len=sam[p].len+1;
	while(p&&(!sam[p].to[c]))sam[p].to[c]=now,p=sam[p].fro;
	last=now;
	if(!p)return sam[now].fro=1,void(0);
	if(sam[p].len+1==sam[sam[p].to[c]].len)return sam[now].fro=sam[p].to[c],void(0);
	int clone=++cnt,qnode=sam[p].to[c];
	sam[clone]=sam[qnode];
	sam[clone].len=sam[p].len+1;
	sam[qnode].fro=sam[now].fro=clone;
	while(p&&sam[p].to[c]==qnode)sam[p].to[c]=clone,p=sam[p].fro;
	return ;
}

signed main(){
	n=rd();
	last=cnt=1;
	for(int i=1;i<=n;i++){
		int x=rd();
		insert(x);
		ans+=sam[last].len-sam[sam[last].fro].len;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @   flywatre  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示