题目描述
给你一个文本串 S 和 n 个模式串 T1..nT_{1..n}T1..n,请你分别求出每个模式串 TiT_iTi 在 S 中出现的次数。
输入格式
第一行包含一个正整数 n 表示模式串的个数。
接下来 n 行,第 i 行包含一个由小写英文字母构成的字符串 TiT_iTi。
最后一行包含一个由小写英文字母构成的字符串 S。
输出格式
输出包含 n 行,其中第 iii 行包含一个非负整数表示 TiT_iTi 在 S 中出现的次数。
输入输出样例
输入 #1
5
a
bb
aa
abaa
abaaa
abaaabaa
输出 #1
6
0
3
2
1
说明/提示
1≤n≤2×1051≤n≤2×10^51≤n≤2×105,T1..nT_{1..n}T1..n 的长度总和不超过 2×1052×10^52×105,S 的长度不超过 2×1062×10^62×106。
#include<bits/stdc++.h>
#define maxn 2000001
using namespace std;
char s[maxn],T[maxn];
int n,cnt,vis[200051],ans,in[maxn],Map[maxn];
struct kkk{
int son[26],fail,flag,ans;
void clear(){memset(son,0,sizeof(son)),fail=flag=ans=0;}
}trie[maxn];
queue<int>q;
void insert(char* s,int num){
int u=1,len=strlen(s);
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!trie[u].son[v])trie[u].son[v]=++cnt;
u=trie[u].son[v];
}
if(!trie[u].flag)trie[u].flag=num;
Map[num]=trie[u].flag;
}
void getFail(){
for(int i=0;i<26;i++)trie[0].son[i]=1;
q.push(1);
while(!q.empty()){
int u=q.front();q.pop();
int Fail=trie[u].fail;
for(int i=0;i<26;i++){
int v=trie[u].son[i];
if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}
trie[v].fail=trie[Fail].son[i]; in[trie[v].fail]++;
q.push(v);
}
}
}
void topu(){
for(int i=1;i<=cnt;i++)
if(in[i]==0)q.push(i);
while(!q.empty()){
int u=q.front();q.pop();vis[trie[u].flag]=trie[u].ans;
int v=trie[u].fail;in[v]--;
trie[v].ans+=trie[u].ans;
if(in[v]==0)q.push(v);
}
}
void query(char* s){
int u=1,len=strlen(s);
for(int i=0;i<len;i++)
u=trie[u].son[s[i]-'a'],trie[u].ans++;
}
int main(){
scanf("%d",&n); cnt=1;
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s,i);
}getFail();scanf("%s",T);
query(T);topu();
for(int i=1;i<=n;i++)printf("%d\n",vis[Map[i]]);
}

本文深入探讨了模式串在文本串中出现次数的高效计算方法,通过构建字典树(Trie)并应用失败指针优化搜索过程,实现快速查找多个模式串在长文本中的位置。文章详细介绍了插入模式串到字典树、获取失败指针、前向遍历字典树进行查询以及逆向更新节点计数等关键步骤。

被折叠的 条评论
为什么被折叠?



