如果你想要做出这道题,你需要先了解两个知识点:
1、字典树的构造
2、KMP算法(也就是fail指针的构造)
对于字典树,可以看看这个大佬:
https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html
对于KMP,可以看看这个大佬:
https://www.cnblogs.com/SYCstudio/p/7194315.html#4255259 (强烈推荐!!!!)
代码实现步骤:(前两个步骤是AC自动机的必备)
1、利用所提供的模式串构造字典树
2、构造fail指针,即当前节点的fail指针 = 它的父节点fail所指向的节点 所指向的相同子节点。,对于定义fail指针的值,借用queue队列的先进先出的思想(此处为难点,不明白的请到评论区)
3、依题意构造query函数,返回个数ans
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e6+5; char t[maxn],s[maxn]; struct tree { int fail;//失配指针 int son[26];//一个节点最多有26个子节点 int num;//标记以此节点为结尾 }DFA[maxn]; //字典树 int cnt=0; void build(string s) { int now=0;//字典树当前指针 for(int i=0;i<s.length();++i) { if(DFA[now].son[s[i]-'a']==0) //树上没有这个子节点 DFA[now].son[s[i]-'a']=++cnt; //新增一个节点 now=DFA[now].son[s[i]-'a']; //向下构造 } DFA[now].num+=1;// 标记单词的结尾 } void Get_fail() //构造fail指针 { queue<int> Q; for(int i=0;i<26;++i) { if(DFA[0].son[i]!=0) { DFA[DFA[0].son[i]].fail=0;//指向根节点,第二层 Q.push(DFA[0].son[i]); //压入队列 } } while(!Q.empty()) { int u=Q.front(); Q.pop(); for(int i=0;i<26;++i) //后面的层 { if(DFA[u].son[i]!=0)//如果存在此节点 { DFA[DFA[u].son[i]].fail=DFA[DFA[u].fail].son[i];//指向该节点的父节点的fail值对应的节点的相同子节点 Q.push(DFA[u].son[i]); //加入队列 } else //如果不存在这个节点 { DFA[u].son[i]=DFA[DFA[u].fail].son[i]; //当前节点的孩子节点指向当前节点的fail的孩子节点 } } } } int query(char *s) { int len=strlen(s); int now=0,ans=0; for(int i=0;i<len;++i) { now=DFA[now].son[s[i]-'a']; for(int t=now;t!=0&&DFA[now].num!=-1;++t) { if(DFA[now].num>0) //表示存在结尾 ans+=DFA[now].num; DFA[t].num=-1; //标记已访问过 } } return ans; } int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%s",t); build(t); } Get_fail(); scanf("%s",s); int ans=query(s); printf("%d ",ans); return 0; }
AC自动机不好理解,原谅我比较懒,没有给大家一个满意的解析,不过大家有问题可以评论区私我,我会尽力为大家解答的!