#219-[后缀自动机]生成魔咒

题目描述

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 次操作加入的魔咒字符。

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制

7
1 2 3 3 3 1 2

输出样例#1: 复制

1
3
6
9
12
17
22

说明

对于%10的数据,1 \le n \le 101≤n≤10

对于%30的数据,1 \le n \le 1001≤n≤100

对于%60的数据,1 \le n \le 1001≤n≤100

对于%100的数据,1 \le n \le 1000001≤n≤100000

用来表示魔咒字符的数字 x 满足1 \le n \le 10^91≤n≤109

(小视野LaTeX复制不了,用了洛谷的)

后缀自动机统计step[i]-step[fail[i]]之和就好

#include <iostream>
#include <cstdio>
#include <map>

using namespace std;
typedef long long ll;
const int MAXN = 200010;

int fail[MAXN], step[MAXN], last=0, id=0; map<int,int> ch[MAXN];

ll append(int x) { // 加入节点
	int np=++id, p=last; // 新建节点
	step[np]=step[p]+1; last=np; // 设置
	while ((p!=-1) && (!ch[p][x])) { // 设儿子,往回条
		ch[p][x]=np; p=fail[p];
	}
	if (p==-1) fail[np]=0; // 没得跑
	else {
		int q = ch[p][x];
		if (step[p]+1==step[q]) fail[np]=q; // 连续
		else { // 不连续
			int nq=++id; step[nq]=step[p]+1; ch[nq]=ch[q]; fail[nq]=fail[q]; fail[q]=fail[np]=nq; // 全部复制,改变原fail等
			while ((p!=-1) && (ch[p][x]==q)) { // 改儿子
				ch[p][x]=nq; p=fail[p];
			}
		}
	}
	//printf("%d %d\n", np, fail[np]);
	return ll(step[np]-step[fail[np]]);
}
int main() {
	fail[0] = -1;
	int n; ll res=0LL; scanf("%d", &n);
	while (n--) {
		int x; scanf("%d", &x);
		printf("%lld\n", res+=append(x));
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值