Codeforces 235C Cyclical Quest 字符串 SAM KMP

原文链接https://www.cnblogs.com/zhouzhendong/p/CF235C.html

题目传送门 -  CF235C

题意

  给定一个字符串 $s$ ,多组询问,每组询问的形式为一个字符串 $T$ ,问 $S$ 有多少个子串与 $T$ 循环同构。(如果 $S$ 有多个相同子串都同构,则算多次)

  $|S|\leq 10^6,\sum |T|\leq 10^6$

题解

  以后坚决不念诗了!中午作死念诗,下午就被一个傻逼错误续了 3 个多钟头。

  做法:

  给 $S$ 建一个 SAM ,然后对于每一个 $T$ KMP 一下,找到最大循环节,在 SAM 上面走一走就可以了。

代码

#include <bits/stdc++.h>
#define right _47hurnf
using namespace std;
typedef long long LL;
const int N=1000005;
int n,m;
char s[N];
struct SAM{
	int Next[26],fa,Max;
}t[N<<1];
int tax[N],id[N<<1],right[N<<1];
int size;
void init(){
	memset(t,0,sizeof t);
	size=1,t[0].Max=-1;
	for (int i=0;i<26;i++)
		t[0].Next[i]=1;
}
int extend(int p,int c){
	if (t[p].Next[c]&&t[p].Max+1==t[t[p].Next[c]].Max)
		return t[p].Next[c];
	int np=++size,q,nq;
	t[np].Max=t[p].Max+1;
	for (;!t[p].Next[c];p=t[p].fa)
		t[p].Next[c]=np;
	q=t[p].Next[c];
	if (t[p].Max+1==t[q].Max)
		t[np].fa=q;
	else {
		nq=++size;
		t[nq]=t[q],t[nq].Max=t[p].Max+1;
		t[q].fa=t[np].fa=nq;
		for (;t[p].Next[c]==q;p=t[p].fa)
			t[p].Next[c]=nq;
	}
	return np;
}
int Fail[N];
int KMP(char s[],int n){
	Fail[0]=Fail[1]=0;
	for (int i=2;i<=n;i++){
		int k=Fail[i-1];
		while (k>0&&s[i]!=s[k+1])
			k=Fail[k];
		Fail[i]=k+(s[k+1]==s[i]?1:0);
	}
	int x=Fail[n];
	while (x>0&&n%(n-x)!=0)
		x=Fail[x];
	return n-x;
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	init();
	for (int i=1,p=1;i<=n;i++)
		p=extend(p,s[i]-'a'),right[p]++;
	for (int i=1;i<=size;i++)
		tax[t[i].Max]++;
	for (int i=1;i<=n;i++)
		tax[i]+=tax[i-1];
	for (int i=1;i<=size;i++)
		id[tax[t[i].Max]--]=i;
	for (int i=size;i>=1;i--)
		right[t[id[i]].fa]+=right[id[i]];
	scanf("%d",&m);
	while (m--){
		scanf("%s",s+1);
		int len=strlen(s+1),r=KMP(s,len);
		LL ans=0;
		int p=1,Matched=0;
		for (int i=1;i<=len;i++){
			int c=s[i]-'a';
			while (!t[p].Next[c])
				p=t[p].fa,Matched=t[p].Max;
			p=t[p].Next[c];
			Matched++;
		}
		for (int i=1;i<=r;i++){
			int c=s[i]-'a';
			while (!t[p].Next[c])
				p=t[p].fa,Matched=t[p].Max;
			p=t[p].Next[c];
			Matched++;
			while (t[t[p].fa].Max>=len)
				p=t[p].fa,Matched=t[p].Max;
			if (Matched>=len)
				ans+=right[p];
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

  

转载于:https://www.cnblogs.com/zhouzhendong/p/CF235C.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值