Description
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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
共有多少种生成魔咒。
Input
第一行一个整数 n。 第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。 1≤n≤100000。,用来表示魔咒字符的数字 x
满足 1≤x≤10^9
Output
输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量
Sample Input
7
1 2 3 3 3 1 2
Sample Output
1
3
6
9
12
17
22
题解
今天仔细想了想SAM的这条性质
状态u表示的本质不同的字符串共有max[u]-min[u]+1个
我觉得很对,于是就A了
因为每次插入只会多length(S)+1个子串,S表示未插入之前的母串
那么我们只用考虑状态u,他表示的本质不同的子串有多少个就行了
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
struct SAM
{
int dep,parent;
map<int,int> son;
}tr[210000];int cnt,last,root;
LL ans;
void add(int x,int op)
{
cnt++;int np=cnt;
int p=last;
tr[np].dep=op;
while(p && tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent;
if(p==0)tr[np].parent=root;
else
{
int q=tr[p].son[x];
if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
else
{
int nq=++cnt;
tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1;
tr[q].parent=tr[np].parent=nq;
while(p && tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
}
}
last=np;
ans+=(LL)tr[np].dep-tr[tr[np].parent].dep;
}
int main()
{
int n;
scanf("%d",&n);
ans=0;root=last=++cnt;
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
add(x,i);
printf("%lld\n",ans);
}
return 0;
}