字符串训练之八
https://www.luogu.org/problem/P3966
分析:
比较普通的AC自动机,这个题唯一不同的就是询问有很多个,但询问的又是模式串
所以建完自动机后暴力询问肯定会Tle
考虑过程跳fail指针的过程
每一个节点保存一下属于多少字符串,为它的权值。
然后一个节点表示的字符串在整个字典中出现的次数相当于其在Fail树中的子树的权值的和
code :
include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1100005
using namespace std;
int n,a[N],h[N],cnt,last,ch[N][26],sz[N],fail[N];
char s[N];
struct ac{
void ins(int x){
scanf("%s",s+1);int now=0,len=strlen(s+1);
for(int i=1;i<=len;i++){
int u=s[i]-'a';
if(!ch[now][u]) ch[now][u]=++cnt;
now=ch[now][u];
sz[now]++;
}
a[x]=now;
}
void build(){
int i,head=0,tail=0;
for(i=0;i<26;i++) if(ch[0][i]) h[++tail]=ch[0][i];
while(head<tail){
int x=h[++head],y;
for(i=0;i<26;i++) if(y=ch[x][i]){
h[++tail]=y;
fail[y]=ch[fail[x]][i];
}
else ch[x][i]=ch[fail[x]][i];
}
}
void solve(){
for(int i=cnt;i>=0;i--) sz[fail[h[i]]]+=sz[h[i]];
for(int i=1;i<=n;i++) printf("%d\n",sz[a[i]]);
}
}ac;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) ac.ins(i);
ac.build();ac.solve();
return 0;
}
当然如果你不会AC自动机还有哈希的做法:
code:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#define f(x,y,z) for(int x=y;x<=z;x++)
#define F(x,y,z,v) for(int x=y;x<=z;x+=v)
using namespace std;
const int MAXN=1000005;
char a[205][MAXN],b[MAXN<<1];
unsigned long long hash1[205],hash2[MAXN<<1],divi[MAXN<<1];
int stl[205];
const int BASE=131;
unsigned long long getsum(int l,int r)
{
return hash2[r]-hash2[l-1]*divi[r-l+1];
}
int main()
{
int n;
scanf("%d",&n);
int cnt=-1,len;
f(i,1,n)
{
b[++cnt]='#';
scanf("%s",&a[i]);
len=strlen(a[i]);
stl[i]=len;
f(j,0,len-1)
{
b[++cnt]=a[i][j];
hash1[i]*=BASE;
hash1[i]+=a[i][j];
}
}
len=strlen(b);
hash2[0]=b[0];
divi[0]=1;
f(i,1,len-1)
{
hash2[i]=hash2[i-1]*BASE+b[i];
divi[i]=divi[i-1]*BASE;
}
f(i,1,n)
{
int tcnt=0;
f(j,1,len-stl[i])
if(hash1[i]==getsum(j,j+stl[i]-1))
tcnt++;
printf("%d\n",tcnt);
}
return 0;
}