给出多个单词
再给出一个字符串
问有多少个单词在字符串里出现过(可能有重复的单词)
思路:trie建树 建fail指针 查询
代码:
#include<bits/stdc++.h>
#define N 500010
using namespace std;
queue<int>q;
struct Aho_Corasick_Automaton {
int c[N][26],val[N],fail[N],cnt;//C是trie树 value是单词个数 fail是指针 cnt是trie中第几个点
void ins(char *s) { //trie建树
int len=strlen(s);
int now=0;
for(int i=0; i<len; i++) {
int v=s[i]-'a';
if(!c[now][v])c[now][v]=++cnt;
now=c[now][v];
}
val[now]++;//以这个点结束的单词数量++
}
void build() {
for(int i=0; i<26; i++)
if(c[0][i])
fail[c[0][i]]=0,q.push(c[0][i]);//将所有的根入队列
while(!q.empty()) {//建立AC 自动机的核心代码
int u=q.front();//当前的点的序号
q.pop();
for(int i=0; i<26; i++)//对这个点的这一层进行遍历
if(c[u][i])//如果这个点存在这个字符,这个点的fail指针指向父节点的这个字母(虽然父节点的这个字母可能不存在)
fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]); //将这个点入队
else c[u][i]=c[fail[u]][i];//不存在这个字符,这个字符指向父亲节点的这个字符
}
}
int query(char *s) {
int len=strlen(s);
int now=0,ans=0;
for(int i=0; i<len; i++) {//对母串进行遍历
now=c[now][s[i]-'a'];//KMP思想
for(int t=now; t&&~val[t]; t=fail[t])//如果这个节点不是根节点 没有被访问过 加上 然后返回它的父节点的其他值加上
ans+=val[t],val[t]=-1;
}
return ans;
}
} AC;
int n;
char p[1000005];
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++)scanf("%s",p),AC.ins(p); //输入一个字符串并且建树
AC.build();//建立自动机
scanf("%s",p);
int ans=AC.query(p);
printf("%d\n",ans);
return 0;
}
我的代码(使用了string):
#include<bits/stdc++.h>
#include<queue>
using namespace std;
#define N 501001
queue<int>q;
struct Auto_Ac{
int trie[N][26],val[N],fail[N],cnt;
void ins(string s){
int len=s.size(),now=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!trie[now][v])trie[now][v]=++cnt;
now=trie[now][v];
}
val[now]++;
}
void build(){//建立fail指针
for(int i=0;i<26;i++)if(trie[0][i])fail[trie[0][i]]=0,q.push(trie[0][i]);
int u;
while(!q.empty()){
u=q.front();
q.pop();
for(int i=0;i<26;i++){
if(trie[u][i])fail[trie[u][i]]=trie[fail[u]][i],q.push(trie[u][i]);
else trie[u][i]=trie[fail[u]][i];//如果这个点不存在值,那么这个点的后续点是父亲的这个点的指向父亲的这个
//点的后续点,指向叔叔 或者堂爷
}
}
}
int ask(string s){
int now=0,ans=0,len=s.size();
for(int i=0;i<len;i++){
now=trie[now][s[i]-'a'];
for(int t=now;t&&~val[t];t=fail[t])
ans+=val[t],val[t]=-1;
}
return ans;
}
}AC;
int main(){
int n;
string s;
scanf("%d",&n);
while(n--){
cin>>s;
AC.ins(s);
}
AC.build();
cin>>s;
cout<<AC.ask(s);
return 0;
}
hdu2222
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int trie[N][26],cnt=0,fail[N],val[N];
void build(char s[]){
int now=0,len=strlen(s),x;
for(int i=0;i<len;i++){
x=s[i]-'a';
if(!trie[now][x])trie[now][x]=++cnt;
now=trie[now][x];
}
val[now]++;
}
void getfail(){
queue<int>q;
for(int i=0;i<26;i++){
if(trie[0][i]){
q.push(trie[0][i]);
fail[trie[0][i]]=0;
}
}
int now=0;
while(!q.empty()){
now=q.front();q.pop();
for(int i=0;i<26;i++){
if(trie[now][i]) fail[trie[now][i]]=trie[fail[now]][i],q.push(trie[now][i]);//失败指针指向祖父的父亲相同节点
else trie[now][i]=trie[fail[now]][i];//失败指针指向 父亲的相同节点
}
}
}
int query(char s[]){
int now=0,ans=0,len=strlen(s);
for(int i=0;i<len;i++){
now=trie[now][s[i]-'a'];
for(int j=now;j&&~val[j];j=fail[j]){
ans+=val[j];
val[j]=-1;
}
}
return ans;
}
int main(){
int n,m;
scanf("%d",&n);
char s1[100],s[1000006];
while(n--){
cnt=0;
memset(trie,0,sizeof(trie));
memset(val,0,sizeof(val));
memset(fail,0,sizeof(fail));
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%s",s1),build(s1);
getfail();
scanf("%s",s);
printf("%d\n",query(s));
}
return 0;
}