【题解】P4070 [SDOI2016]生成魔咒(SAM/后缀自动机)
[SDOI2016]生成魔咒
题目描述
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 拼凑起来形成一个魔咒串 。
一个魔咒串 的非空字串被称为魔咒串 的生成魔咒。
例如 时,它的生成魔咒有 五种。 时,它的生成魔咒有 三种,最初 S 为空串。
共进行 次操作,每次操作是在 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 共有多少种生成魔咒。
输入格式
第一行一个整数 。
第二行 个数,第 个数表示第 次操作加入的魔咒字符 。
输出格式
输出 行,每行一个数。
第 行的数表示第 次操作后 的生成魔咒数量
样例 #1
样例输入 #1
7
1 2 3 3 3 1 2
样例输出 #1
1
3
6
9
12
17
22
提示
数据规模与约定
对于 的数据,保证 ;
对于 的数据,保证 ;
对于 的数据,保证 ;
对于 的数据,保证 ,。
题解
考虑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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!