P4070 [SDOI2016]生成魔咒(SAM len数组的含义)

[SDOI2016]生成魔咒

题目描述

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1 , 2 1,2 1,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 ] , [ 2 ] , [ 1 , 2 ] , [ 2 , 1 ] , [ 1 , 2 , 1 ] [1],[2],[1,2],[2,1],[1,2,1] [1],[2],[1,2],[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 为空串。

共进行 n n n 次操作,每次操作是在 S S S 的结尾加入一个魔咒字符。每次操作后都需要求出,当前的魔咒串 S S S 共有多少种生成魔咒。

输入格式

第一行一个整数 n n n

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

输出格式

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

样例 #1

样例输入 #1

7
1 2 3 3 3 1 2

样例输出 #1

1
3
6
9
12
17
22

提示

数据规模与约定

对于 10 % 10\% 10% 的数据,保证 1 ≤ n ≤ 10 1 \le n \le 10 1n10
对于 30 % 30\% 30% 的数据,保证 1 ≤ n ≤ 100 1 \le n \le 100 1n100
对于 60 % 60\% 60% 的数据,保证 1 ≤ n ≤ 1 0 3 1 \le n \le 10^3 1n103
对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1n105 1 ≤ x i ≤ 1 0 9 1 \leq x_i \leq 10^9 1xi109

题意:

给定 n 次插入字符串操作,要求求出每次插入一个字符时,当前字符串包含的本质不同子串的数量。

思路:

我们知道,后缀链接树上存储了一个字符串的所有子串,我们可以把当前的答案看做是树上所有节点包含的子串数量总和,树上每个节点 u 包含的子串数量为:len[u] - len[fa[u]],当插入一个字符,给答案造成了贡献,我们等价地看成是新创建了个节点 np,这个操作对于答案的贡献为即为 len[np] - len[fa[np]]。

因此具体做法就是,每进行extend一个字符的时候,对于答案累加上应有的贡献即可。

时间复杂度:

O ( n ) O(n) O(n)

代码:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1e5 + 10, M = N << 1;
int n, fa[M], len[M], tot = 1, np = 1;
unordered_map<int, unordered_map<int, int>> ch;
vector<int> g[M];
string s;
ll sum = 0;

void extend(int c)
{
	int p = np; np = ++tot;
	len[np] = len[p] + 1;
	while (p && !ch[p][c]) {
		ch[p][c] = np;
		p = fa[p];
	}
	if (!p) {
		fa[np] = 1;
	}
	else {
		int q = ch[p][c];
		if (len[q] == len[p] + 1) {
			fa[np] = q;
		}
		else {
			int nq = ++tot;
			len[nq] = len[p] + 1;
			fa[nq] = fa[q], fa[q] = fa[np] = nq;
			while (p && ch[p][c] == q) {
				ch[p][c] = nq;
				p = fa[p];
			}
			ch[nq] = ch[q];
		}
	}
	sum += (len[np] - len[fa[np]]);
}

signed main()
{
	cin >> n;
	for (int i = 0; i < n; ++i) {
		int c; cin >> c;
		extend(c);
		printf("%lld\n", sum);
	}
	

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值