http://codeforces.com/problemset/problem/123/D
题意:给你一个字符串S,定义一个函数F(S,x),其中x为S中的字串,F(S,x)的定义太麻烦啦,其实就是设x在S中出现了n次,则F(S,x)=n*(n+1)/2。要你求对于所有的x,F(S,x)的和。
思路:裸的后缀自动机吧,首先设num为SAM中一个状态所表示的子串的出现次数,这个由拓扑排序然后自底向上更新即可,然后对于每一个状态(设为p),它所表示的串一共有p->val-p->par->val+1个,然后再乘上p->num*(p->num+1)/2就得到了对于状态p中F(S,x)的和。(这里的x是p状态所标示的子串),然后对于每一个状态都这么求,就可以得到答案了。因为SAM的状态数是O(n)的,所以时间复杂度也为O(n)的。下面是代码。
#include <iostream>
#include <string.h>
#include <stdio.h>
#define maxn 200010
#define Smaxn 26
#define inf 21000000
using namespace std;
struct node
{
node *par,*go[Smaxn];
int num;
int val;
}*root,*tail,que[maxn],*top[maxn];
int tot;
char str[100010];
void add(int c,int l)
{
node *p=tail,*np=&que[tot++];
np->val=l;
while(p&&p->go[c]==NULL)
p->go[c]=np,p=p->par;
if(p==NULL) np->par=root;
else
{
node *q=p->go[c];
if(p->val+1==q->val) np->par=q;
else
{
node *nq=&que[tot++];
*nq=*q;
nq->val=p->val+1;
np->par=q->par=nq;
while(p&&p->go[c]==q) p->go[c]=nq,p=p->par;
}
}
tail=np;
}
int c[maxn],len;
void init()
{
memset(que,0,sizeof(que));
tot=0;
len=1;
root=tail=&que[tot++];
}
void solve()
{
memset(c,0,sizeof(c));
int i;
for(i=0;i<tot;i++)
c[que[i].val]++;
for(i=1;i<len;i++)
c[i]+=c[i-1];
for(i=0;i<tot;i++)
top[--c[que[i].val]]=&que[i];
for(node *p=root;;p=p->go[str[p->val]-'a'])
{
p->num=1;
if (p->val==len-1)break;
}
for(i=tot-1;i>=0;i--)
{
node *p=top[i];
if(p->par)
{
node *q=p->par;
q->num+=p->num;
}
}
long long ans=0;
for(i=1;i<tot;i++)
{
node *p=top[i];
int tmp=p->val-p->par->val,sum=p->num;
ans+=(long long)tmp*((long long)sum*(sum+1)/2);
}
printf("%I64d\n",ans);
}
int main()
{
freopen("dd.txt","r",stdin);
scanf("%s",str);
int l=strlen(str),i;
init();
for(i=0;i<l;i++)
{
add(str[i]-'a',len++);
}
solve();
return 0;
}