Codeforces 235C Cyclical Quest 字符串 SAM KMP

时间:2021-07-25 05:45:34

原文链接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;
}