cunzai_zsy0531

关注我

P5341 [TJOI2019]甲苯先生和大中锋的字符串 题解

题面

拿出出现次数为 \(k\) 的点,给这个点表示的长度区间 \(+1\),这个可以使用差分解决。

点击查看代码
const int N=1e5+13;
char s[N];
int nxt[N<<1],len[N<<1],ptot,lastpos,ind[N<<1];
ll cnt[N<<1],a[N];
std::unordered_map<int,int> son[N<<1],boom;
inline int newpos(std::unordered_map<int,int> nson,int nlen){return len[++ptot]=nlen,std::swap(son[ptot],nson),ptot;}
inline void insert(int c){
	int p=lastpos;int u=newpos(boom,len[p]+1);cnt[u]=1;
	while(p&&son[p].find(c)==son[p].end()) son[p][c]=u,p=nxt[p];
	lastpos=u;
	if(!p) return nxt[u]=1,void();
	int d=son[p][c];
	if(len[d]==len[p]+1) nxt[u]=d;
	else{
		int v=newpos(son[d],len[p]+1);cnt[v]=0;
		nxt[v]=nxt[d],nxt[d]=nxt[u]=v;
		while(p&&son[p][c]==d) son[p][c]=v,p=nxt[p];
	}
}
inline void clear(){
	ptot=lastpos=1;
	memset(a,0,sizeof a);memset(ind,0,sizeof ind);
	std::unordered_map<int,int> zrzak;std::swap(zrzak,son[1]);
}
int main(){int T;read(T);while(T--){
	clear();
	read(s+1);int n=strlen(s+1),k;
	read(k);
	for(int i=1;i<=n;++i) insert(s[i]-'a');
	for(int i=2;i<=ptot;++i) ind[nxt[i]]++;
	std::queue<int> q;
	for(int i=2;i<=ptot;++i)
		if(!ind[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		if(nxt[u]==1) continue;
		cnt[nxt[u]]+=cnt[u];
		if(!(--ind[nxt[u]])) q.push(nxt[u]);
	}
	for(int i=2;i<=ptot;++i)
		if(cnt[i]==k) a[len[nxt[i]]+1]++,a[len[i]+1]--;
	int ans=-1,maxx=1;
	for(int i=1;i<=n;++i){
		a[i]+=a[i-1];
		if(a[i]>=maxx) maxx=a[i],ans=i;
	}
	println(ans);
}
	return 0;
}
posted @ 2022-05-18 21:01  cunzai_zsy0531  阅读(46)  评论(0编辑  收藏  举报