标签:continue for 思路 namespace div closed 遍历 模板 不用
题意:给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
思路:建完tire,fail之后,开始跑自动机,然后对于走过的点,给一个标记,防止多次计算即可
为啥呢,因为求得是有多少模式串在文本串出现过,而不是出现次数
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1000001; 4 struct AC{ 5 int son[26]; 6 int flag,fail; 7 }trie[maxn]; 8 int n,cnt; 9 char s[maxn]; 10 queue<int>q; 11 void Insert(char* s) 12 { 13 int u=1,len=strlen(s); 14 for(int i=0;i<len;i++){ 15 int v=s[i]-‘a‘; 16 if(!trie[u].son[v]) 17 trie[u].son[v]=++cnt; 18 u=trie[u].son[v]; 19 } 20 trie[u].flag++; 21 } 22 void getFail() 23 { 24 for(int i=0;i<26;i++) 25 trie[0].son[i]=1; //初始化0的所有儿子都是1 26 q.push(1);trie[1].fail=0; //将根压入队列 27 while(!q.empty()){ 28 int u=q.front();q.pop(); 29 for(int i=0;i<26;i++){ //遍历所有儿子 30 int v=trie[u].son[i]; //处理u的i儿子的fail,这样就可以不用记父亲了 31 int Fail=trie[u].fail; //就是fafail,trie[Fail].son[i]就是和v值相同的点 32 if(!v){ 33 trie[u].son[i]=trie[Fail].son[i]; 34 continue; 35 } //不存在该节点,第二种情况 36 trie[v].fail=trie[Fail].son[i]; //第三种情况,直接指就可以了 37 q.push(v); //存在实节点才压入队列 38 } 39 } 40 } 41 int query(char* s) 42 { 43 int u=1,ans=0,len=strlen(s); 44 for(int i=0;i<len;i++){ 45 int v=s[i]-‘a‘; 46 int k=trie[u].son[v]; //跳Fail 47 while(k>1&&trie[k].flag!=-1){ //经过就不统计了 48 ans+=trie[k].flag; //累加上这个位置的模式串个数 49 trie[k].flag=-1; //标记已经过 50 k=trie[k].fail; //继续跳Fail 51 } 52 u=trie[u].son[v]; //到下一个儿子 53 } 54 return ans; 55 } 56 int main(){ 57 cnt=1;scanf("%d",&n); 58 for(int i=1;i<=n;i++){ 59 scanf("%s",s); 60 Insert(s); 61 } 62 getFail(); 63 scanf("%s",s); 64 printf("%d\n",query(s)); 65 return 0; 66 }
标签:continue for 思路 namespace div closed 遍历 模板 不用
原文地址:https://www.cnblogs.com/pangbi/p/12667116.html