SPOJ:Substrings(后缀自动机)

本文介绍了一种使用后缀自动机解决特定字符串问题的方法:对于给定长度为N的字符串,求出每个长度i(1到N)上出现次数最多的不同子串的频数,并详细展示了具体的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as the maximal number of times that some string with length x appears in S. For example for string 'ababa' F(3) will be 2 because there is a string 'aba' that occurs twice. Your task is to output F(i) for every i so that 1<=i<=|S|.

Input

String S consists of at most 250000 lowercase latin letters.

Output

Output |S| lines. On the i-th line output F(i).

Example

Input:
ababa

Output:
3
2
2
1
1

题意:给一个串,设长度为N,对于每个i(1->N),对于所有长度为i的不同的子串,F[i]为这些子串中出现次数最多的值,输出F[i]。

思路:后缀自动机,需要计算每个集合的right值(子串终点集合),那么可以对这些集合进行拓扑排序,然后累加,最后倒着维护最大值输出。

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int N = 500000+30;
char s[N];
int cnt,last;
int b[N],fa[N],mx[N],r[N],ch[N][26],f[N], id[N], ans[N];
void add(int x)
{
	int c = s[x]-'a';
	int p = last, np = ++cnt; last = np;
	mx[np] = x;
	for(; p&&!ch[p][c]; p=fa[p]) ch[p][c] = np;
	if(!p) fa[np] = 1;
	else
	{
		int q = ch[p][c];
		if(mx[p]+1 == mx[q]) fa[np] = q;
		else
		{
			int nq=++cnt; mx[nq] = mx[p] + 1;
			memcpy(ch[nq], ch[q], sizeof ch[q]);
			fa[nq] = fa[q];
			fa[np] = fa[q] = nq;
			for(; p&&ch[p][c]==q; p=fa[p]) ch[p][c] = nq;
		}
	}
}
int main()
{
    last = ++cnt;
    scanf("%s",s+1);
    int len = strlen(s+1);
    for(int i=1; i<=len; ++i) add(i);
    for(int i=1,p=1; i<=len; ++i)
        p = ch[p][s[i]-'a'],++f[p];//主链加上1,表示至少出现1次
    for(int i=1; i<=cnt; ++i) ++b[mx[i]];//基数排序
	for(int i=1; i<=len; ++i) b[i] += b[i-1];
	for(int i=1; i<=cnt; ++i) id[b[mx[i]]--]=i;//排在第i的是哪个集合
	for(int i=cnt; i; --i) f[fa[id[i]]] += f[id[i]];//按拓扑序累加
	for(int i=1; i<=cnt; ++i) ans[mx[i]] = max(ans[mx[i]], f[i]);
	for(int i=len; i; --i) ans[i]=max(ans[i+1],ans[i]);
	for(int i=1; i<=len; ++i)printf("%d\n",ans[i]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值