题意:
给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。
求F(1)..F(Length(S)) Length(S) <= 250000
思路:我们用每个状态的len来更新答案,因为所有状态即可包含所有的子串。
坑点:我原来一直以为不需要进行基数排序,直到这题。。。如果不基数排序的话,clone 就是一个范例,他的儿子的标号小于他本身。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 250005;
int righ[maxn<<1];
const int ch_size = 26;
struct SAM{
int fa[maxn<<1], // 后缀链接
ch[maxn<<1][ch_size],
len[maxn<<1], //该节点最长串的长度
tot, // 节点总数
last; // 代表当前的整个串
void init(){
tot = last = 0;
fa[0] = -1;
len[0] = 0;
memset( ch[0],0,sizeof(ch[0]) );
}
void extend( int x ){
int p = last;
int cur = ++tot;
memset( ch[cur],0,sizeof( ch[cur] ) );
len[cur] = len[last]+1;
while( p != -1 && !ch[p][x] ){
ch[p][x] = cur;
p = fa[p];
}
if( p == -1 ){
fa[cur] = 0;
}else {
int q = ch[p][x];
if (len[q] == len[p] + 1) {
fa[cur] = q;
} else {
int clone = ++tot;
len[clone] = len[p] + 1;
for (int i = 0; i < ch_size; i++) {
ch[clone][i] = ch[q][i];
}
fa[clone] = fa[q];
fa[q] = fa[cur] = clone;
while (p != -1) {
if (ch[p][x] == q)ch[p][x] = clone;
else break;
p = fa[p];
}
}
}
last = cur;
}
}g;
char str[maxn];
int ans[maxn],len,c[maxn];
int b[maxn<<1];
void radix(){
for( int i = 1;i <= g.tot;i++ ) c[ g.len[i] ]++;
for( int i = 2;i <= len;i++ ) c[i] += c[i-1];
for( int i = 1;i <= g.tot;i++ ) b[ c[ g.len[i] ]-- ] = i;
}
int main(){
g.init();
scanf("%s",str);
len = strlen(str);
for( int i = 0;i < len;i++ ){
g.extend( str[i]-'a' );
}
int p = 0;
for( int i = 0;i < len;i++ ){
int c= str[i]-'a';
p = g.ch[p][c];
righ[p] = 1;
}
radix();
for( int i = g.tot;i > 1;i-- ){
righ[ g.fa[ b[i] ] ] += righ[ b[i] ];
}
for( int i = 1;i <= g.tot;i++ ){
ans[g.len[i] ] = max(ans[ g.len[i] ],righ[i]);
}
for( int i = len-1;i >= 1;i-- ){
ans[i] = max( ans[i],ans[i+1] );
}
for( int i = 1;i <= len;i++ ){
printf("%d\n",ans[i]);
}
return 0;
};