Time Limit:15000ms
Case Time Limit:3000ms
Memory Limit:512MB
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。
现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数。但是K不是固定的,小Hi想知道对于所有的K的答案。
输入
共一行,包含一个由小写字母构成的字符串S。字符串长度不超过 1000000。
输出
共Length(S)行,每行一个整数,表示答案。
Sample Input
aab
Sample Output
2 1 1
讲解推荐这位大佬的:传送门
代码:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 1000005;
typedef struct Node
{
int len, pre;//pre:对应图片中的绿线(一个节点显然最多只有一条绿线)
int Next[26];//Next[]:对应图片中的蓝线
}Node;
Node tre[MAXN*2];
vector<int> G[MAXN*2];//用于建Suffix Links树
int cnt, last, siz[MAXN*2], ans[MAXN*2];
char str[MAXN];
void Insert(char ch)
{
int p, q, now, rev;
p = last, now = cnt++;
tre[now].len = tre[last].len+1;
siz[now]++; //如果节点u包含子串S[1..i],那么满足|siz(u)| = ∑|siz(son(u))|+1,这里先加上那个1,这样的话对于SL树就可以直接求和了
while(p!=-1 && tre[p].Next[ch-'a']==0) //每次跳到当前最长且siz集合与当前不同的后缀上,对应图片中的绿线回退(例如7→8→5→S)
{
tre[p].Next[ch-'a'] = now; //tran(st[p], ch)=now,对应图片中的蓝线连接
p = tre[p].pre;
}
if(p==-1)
tre[now].pre = 0; //情况①,递归到了初始节点S(空子串)(例如图片中的9号节点pre[9]=0)
else //如果中途某个子串tran(st[p], ch)已经存在
{
q = tre[p].Next[ch-'a'];
if(tre[q].len==tre[p].len+1) //情况②:节点q的最长子串刚好就是节点p的最长子串+S[i],也就是len[q] = len[p]+1
tre[now].pre = q;
else //情况③
{
rev = cnt++;
tre[rev] = tre[q];
tre[rev].len = tre[p].len+1; //这三行就是对Suffix Links内向树的插点操作
tre[q].pre = tre[now].pre = rev;
while(p!=-1 && tre[p].Next[ch-'a']==q)
{
tre[p].Next[ch-'a'] = rev;
p = tre[p].pre;
}
}
}
last = now;
}
void SechSL(int u) //求出所有节点的|endpos()|
{
int v;
for(int i=0 ; i<G[u].size() ; ++i)
{
v = G[u][i];
SechSL(v);
siz[u] += siz[v];
}
ans[tre[u].len] = max(ans[tre[u].len], siz[u]);
}
inline void Init()
{
cnt = last = 0;
memset(tre, 0, sizeof(tre));
for(int i=0 ; i<MAXN*2 ; ++i)G[i].clear();
tre[cnt++].pre = -1;
}
int main()
{
int n;
scanf("%s", str+1);
n = strlen(str+1);
Init();
for(int i=1 ; i<=n ; ++i)Insert(str[i]);
for(int i=1 ; i<=cnt-1 ; ++i)G[tre[i].pre].push_back(i);//建树
SechSL(0);
/*---------------------------
LL ans = 0;
for(i=1;i<=cnt-1;i++)
ans += tre[i].len-tre[tre[i].pre].len; //求出有多少个本质不同的子串
-----------------------------*/
for(int i=n ; i>=1 ; i--)
ans[i] = max(ans[i], ans[i+1]);
for(int i=1 ; i<=n ; ++i)
printf("%d\n", ans[i]);
return 0;
}