题目链接
题意就是给n 个单词,然后给你一个文本串。问在这个文本串中出现这n个单词的数量。
用一个val[i]保存i节点结尾的单词个数就可以了。
两种模板:
第一种来自我之前的博客:博客
#include<bits/stdc++.h>
using namespace std;
const int M=60,N=1e6+10;
char s[N];
struct ac_auto
{
int ne[N][26],val[N],fail[N],sz;
void init()
{
memset(ne[0],0,sizeof(ne[0]));
sz=0;
}
void insert(char *s)//字典树部分
{
int o=0;
for(int i=0;s[i];++i)
{
int c=s[i]-'a';
if(ne[o][c]==0){
ne[o][c]=++sz;
val[sz]=0;
memset(ne[sz],0,sizeof(ne[sz]));
}
o=ne[o][c];
}
val[o]++;
}
void build()//bfs处理fail指针,指向深度小于自己的字符相同 最近的那个节点
{
queue<int>que;
for(int i=0;i<26;++i)
if(ne[0][i]){
que.push(ne[0][i]);
fail[ne[0][i]]=0;//第一层节点的fail指针指向根节点
}
while(que.size())
{
int o=que.front();que.pop();
for(int i=0;i<26;++i){
if(ne[o][i])
{
int v=ne[o][i];
int fa=fail[o];
while(fa&&!ne[fa][i]) fa=fail[fa];//如果当前节点没有 i 号儿子,继续跳fail指针,直到根或者有 i 号节点
fail[v]=ne[fa][i];
que.push(v);
}
}
}
}
int query(char *s)//查询 类字典树查询套 跳fail指针 保存答案
{
int o=0,ans=0;
for(int i=0;s[i];++i)
{
int c=s[i]-'a';
while(!ne[o][c]&&o) o=fail[o];
o=ne[o][c];
int tmp=o;
while(tmp)
{
ans+=val[tmp];
val[tmp]=0;
tmp=fail[tmp];
}
}
return ans;
}
}ac;
int main()
{
int _;cin>>_;while(_--)
{
int n;
scanf("%d",&n);
ac.init();
for(int i=1;i<=n;++i){
scanf("%s",s);
ac.insert(s);
}
ac.build();
scanf("%s",s);
printf("%d\n",ac.query(s));
}
}
第二种模板:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n, m;
char s[N];
struct node
{
int tr[N][26], fail[N], num[N], cnt = 0;
void init(){
memset(tr[0], 0, sizeof(tr[0])), cnt=0;
}
void insert(char s[]){
int n = strlen(s + 1), root = 0;
for(int i = 1; i <= n; ++i){
if(!tr[root][s[i] - 'a']) {
tr[root][s[i] - 'a'] = ++cnt;
memset(tr[cnt], 0, sizeof(tr[cnt])), num[cnt] = 0;
}
root = tr[root][s[i]-'a'];
}
//printf("cnt:%d root:%d\n", cnt, root);
num[root]++;
}
void getfail(){
queue<int> que;
for(int i = 0; i < 26; ++i){
if(tr[0][i]) que.push(tr[0][i]), fail[tr[0][i]] = 0;
}
while(que.size()){
int now = que.front(); que.pop();
for(int i = 0; i < 26; ++i){
if(tr[now][i]) fail[tr[now][i]] = tr[fail[now]][i], que.push(tr[now][i]);
else tr[now][i] = tr[fail[now]][i];
}
}
}
int find(char s[]){
int ans = 0;
int root = 0, len = strlen(s+1);
for(int i = 1; i <= len; ++i){
root = tr[root][s[i] - 'a'];
int tmp = root;
while(tmp){
//printf("tmp:%d\n",tmp);
ans += num[tmp];
num[tmp] = 0;
tmp = fail[tmp];
}
}
return ans;
}
}ac;
int main()
{
int _;scanf("%d", &_);while(_--){
ac.init();
scanf("%d", &n);
for(int i = 1; i <= n; ++i){
scanf("%s", s + 1);
ac.insert(s);
}
ac.getfail();
scanf("%s", s+1);
printf("%d\n", ac.find(s));
}
return 0;
}
两种模板的区别 在于构建fail指针的时候不同
相比来说第二种会比第一种快一点(个人理解),因为构建fail指针的时候 中间没有反复跳fail