【洛谷P5341】甲苯先生和大中锋的字符串

题目

题目链接:https://www.luogu.com.cn/problem/P5341
大中锋有一个长度为 \(n\) 的字符串,他只知道其中的一个子串是祖上传下来的宝藏的密码。但是由于字符串很长,大中锋很难将这些子串一一尝试。
这天大中锋找到甲苯先生算命,但是甲苯先生说:“天机不可泄漏”。
在大中锋的苦苦哀求下,甲苯先生告诉大中锋:“密码是在字符串中恰好出现了 \(k\) 次的子串”。
但是大中锋不知道该怎么做,在大中锋再三的恳求下,甲苯先生看其真诚,又告诉他:“在恰好出现了 \(k\) 次的子串中,你去按照字串的长度分类,密码就在数量最多的那一类里”。
大中锋为了尝试这个密码,想让你帮忙找出子串长度出现次数最多的长度数(如果有多个输出最长长度)。
\(1\leq n\leq 10^5,1 \leq T \leq 100,\sum n \leq 3 * 10^6\)

思路

氵氵氵。
恰好出现 \(k\) 次的子串显然等价于 \(\mathrm{endpos}\) 集合大小等于 \(k\) 的子串,parent 树上乱搞就可以得到每一个节点 \(\mathrm{endpos}\) 集合的大小。
然后如果一个等价类的出现次数恰好为 \(k\),那么直接用差分把 \(cnt_{\mathrm{len[fa]+1}}\sim cnt_{\mathrm{len[x]}}\) 全部加一。最后在 \(cnt\) 中找最大值即可。
时间复杂度 \(O(\sum n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=200010;
int Q,n,m,ans,cnt[N],a[N],c[N];
char s[N];

struct SAM
{
	int tot,last,ch[N][26],fa[N],len[N],siz[N];
	
	void ins(int c)
	{
		int p=last,np=++tot;
		last=tot; len[np]=len[p]+1; siz[np]=1;
		for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np;
		if (!p) fa[np]=1;
		else
		{
			int q=ch[p][c];
			if (len[q]==len[p]+1) fa[np]=q;
			else
			{
				int nq=++tot;
				fa[nq]=fa[q]; len[nq]=len[p]+1;
				for (int i=0;i<26;i++) ch[nq][i]=ch[q][i];
				fa[q]=fa[np]=nq;
				for (;p && ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
			}
		}
	}
	
	void topsort()
	{
		for (int i=0;i<=tot;i++) c[i]=0;
		for (int i=1;i<=tot;i++) c[len[i]]++;
		for (int i=1;i<=tot;i++) c[i]+=c[i-1];
		for (int i=1;i<=tot;i++) a[c[len[i]]--]=i;
		for (int i=tot;i>=1;i--)
		{
			int j=a[i];
			siz[fa[j]]+=siz[j];
			if (j!=1 && siz[j]==m) cnt[len[fa[j]]+1]++,cnt[len[j]+1]--;
		}
	}
}sam;

void prework()
{
	for (int i=0;i<=sam.tot;i++)
	{
		sam.fa[i]=sam.len[i]=sam.siz[i]=0;
		memset(sam.ch[i],0,sizeof(sam.ch[i]));
	}
	sam.tot=sam.last=1;
	for (int i=0;i<=n;i++) cnt[i]=0;
}

int main()
{
	scanf("%d",&Q);
	while (Q--)
	{
		prework();
		scanf("%s%d",s+1,&m);
		n=strlen(s+1);
		for (int i=1;i<=n;i++)
			sam.ins(s[i]-'a');
		sam.topsort();
		ans=0;
		for (int i=1;i<=n;i++)
		{
			cnt[i]+=cnt[i-1];
			if (cnt[i] && cnt[i]>=cnt[ans]) ans=i;
		}
		if (ans) printf("%d\n",ans);
			else printf("-1\n");
	}
	return 0;
}
posted @ 2021-01-20 20:28  stoorz  阅读(75)  评论(0编辑  收藏  举报