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 21000000using 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;}