题目大意:
给你n个数,每个数代表一个不同的字符。
初始为空串,每次在里面插入一个字符,并且询问本质不同的子串的个数。
题目分析:(后缀自动机)
后缀自动机想要表示所有的后缀以及后缀的前缀,就会有性质:min_len=fa->max _len+1。
并且max_len-min _len+1就代表当前节点所表示的本质不同的子串的个数。
(这些后缀自动机的性质还需要我们感性的理解啊……)
所以我们在每次插入的时候维护一下更改节点的max_len值,并且把答案加上max _len- fa->max _len,输出答案就可以了。
注意事项:
1、因为q的father改变了,所以先给他的max_len减掉,再在后面给它加回来;
2、因为你并不知道一共有多少个字符,所以用map存儿子;
3、答案用long long输出。
代码如下:
#include<cstdio>
#include<map>
using namespace std;
int n;
long long ans;
struct SAM{
map<int,SAM*> son;
SAM *fa;
int max_len;
SAM(int _=0):fa(NULL),max_len(_){}
}*root=new SAM,*last=root;
void extend(int x)
{
SAM *p=last;
SAM *np=new SAM(p->max_len+1);
while(p && !p->son[x])
p->son[x]=np,p=p->fa;
if(!p) np->fa=root;
else
{
SAM *q=p->son[x];
if(p->max_len+1==q->max_len) np->fa=q;
else
{
SAM *nq=new SAM(p->max_len+1);
ans-=q->max_len-q->fa->max_len;
nq->fa=q->fa;
nq->son=q->son;
q->fa=nq;np->fa=nq;
for(;p && p->son[x]==q;p=p->fa) p->son[x]=nq;
ans+=q->max_len-q->fa->max_len;
ans+=nq->max_len-nq->fa->max_len;
}
}
last=np;
ans+=np->max_len-np->fa->max_len;
}
int main()
{
scanf("%d",&n);
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
extend(x);
printf("%lld\n",ans);
}
return 0;
}