题目描述
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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;
}