题目链接:http://www.spoj.com/problems/NSUBSTR/
题意:给定一个串S,定义函数f(x)表示长度为x的所有子串中出现次数最多的子串出现的次数。输出f(i)(1<=i<=len(S))。
思路:建立串S的后缀自动机。然后就是对于每个节点u,从u开始到达接收状态的路径的个数就是u节点对应串的出现次数。
const int KIND=26;
struct SAM
{
SAM *son[KIND],*pre;
int len,cnt;
void init()
{
clr(son,NULL);
pre=NULL;
len=0;
}
};
SAM sam[N],*head,*last,*b[N];
int d[N],cnt,f[N],len;
char s[N];
void initSAM()
{
sam[0].init();
head=last=&sam[0];
cnt=1;
}
void insert(int x)
{
SAM *p=&sam[cnt++],*u=last;
p->len=last->len+1;
last=p;
for(;u&&!u->son[x];u=u->pre) u->son[x]=p;
if(!u) p->pre=head;
else if(u->son[x]->len==u->len+1) p->pre=u->son[x];
else
{
SAM *r=&sam[cnt++],*q=u->son[x];
*r=*q; r->len=u->len+1;
p->pre=q->pre=r;
for(;u&&u->son[x]==q;u=u->pre) u->son[x]=r;
}
}
int main()
{
RD(s);
len=strlen(s);
initSAM();
int i;
FOR0(i,len) insert(s[i]-'a');
FOR0(i,cnt) d[sam[i].len]++;
FOR1(i,len) d[i]+=d[i-1];
FOR0(i,cnt) b[--d[sam[i].len]]=&sam[i];
last=head;
FOR0(i,len)
{
last=last->son[s[i]-'a'];
last->cnt=1;
}
FORL1(i,cnt-1)
{
f[b[i]->len]=max(f[b[i]->len],b[i]->cnt);
b[i]->pre->cnt+=b[i]->cnt;
}
FORL1(i,len-1) f[i]=max(f[i],f[i+1]);
FOR1(i,len) PR(f[i]);
return 0;
}