题意:每组数据中有n个子串,给出目标串,求出共有多少个子串在目标串中出现过,输出数量。
题解:妥妥的ac自动机(多模匹配算法),是个模板题。
ac自动机学习参考链接(大佬的图画的太棒了):
https://blog.csdn.net/weixin_40317006/article/details/81327188
https://blog.csdn.net/creatorx/article/details/7110084
链表版
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 #include<malloc.h> 5 using namespace std; 6 7 const int maxn=1e7+10; 8 9 struct node 10 { 11 node *next[26]; //下一个字母 12 node *fail; //失败指针 13 int sum; //几个单词结点 14 }; 15 16 node *root; 17 node *newnode; 18 int head,tail; 19 node *q[maxn]; 20 char pattern[maxn]; 21 char key[100]; 22 int cnt; 23 24 void insert(char *s) 25 { 26 node *p=root; 27 for(int i=0;s[i];i++){ 28 int x=s[i]-'a'; 29 if(p->next[x]==NULL){ 30 newnode=(struct node*)malloc(sizeof(struct node)); 31 for(int j=0;j<26;j++) newnode->next[j]=0; 32 newnode->sum=0; 33 newnode->fail=0; 34 p->next[x]=newnode; 35 } 36 p=p->next[x]; 37 } 38 p->sum++; 39 } 40 41 void build_fail() 42 { 43 head=0; 44 tail=1; 45 q[head]=root; 46 node *p;node *temp; 47 while(head<tail){ 48 temp=q[head++]; 49 for(int i=0;i<26;i++){ 50 if(temp->next[i]){ 51 if(temp==root){ 52 temp->next[i]->fail=root; 53 } 54 else{ 55 p=temp->fail; 56 while(p){ 57 if(p->next[i]){ 58 temp->next[i]->fail=p->next[i]; 59 break; 60 } 61 p=p->fail; 62 } 63 if(!p) temp->next[i]->fail=root; 64 } 65 q[tail++]=temp->next[i]; 66 } 67 } 68 } 69 } 70 71 void ac_chicken(char *ch) 72 { 73 node *p=root; 74 int len=strlen(ch); 75 for(int i=0;i<len;i++){ 76 int x=ch[i]-'a'; 77 while(!p->next[x]&&p!=root) p=p->fail; //找到这个字母 78 p=p->next[x]; //p到这个节点 79 if(!p) p=root; //没有的话就是根节点 80 node *temp=p; 81 while(temp!=root){ 82 if(temp->sum){ 83 cnt+=temp->sum; 84 temp->sum=0; 85 } 86 else break; 87 temp=temp->fail; 88 } 89 } 90 } 91 92 int main() 93 { 94 int t,n; 95 scanf("%d",&t); 96 while(t--){ 97 root=(struct node*)malloc(sizeof(struct node)); 98 for(int i=0;i<26;i++) root->next[i]=0; 99 root->fail=0; 100 root->sum=0; 101 cnt=0; 102 scanf("%d",&n); 103 for(int i=0;i<n;i++){ 104 scanf("%s",key); 105 insert(key); 106 } 107 build_fail(); 108 scanf("%s",pattern);
109 ac_chicken(pattern);
110 printf("%d\n",cnt); 111 } 112 return 0; 113 }
数组版(一定记得初始化!!不初始化会mle 鬼知道为什么-_-!!)
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 #include<queue> 5 using namespace std; 6 7 const int maxn=1e6+10; 8 int n,cnt=1; 9 int tree[maxn][26],color[maxn],fail[maxn]; 10 char pattern[2*maxn]; 11 char key[100]; 12 queue<int> q; 13 14 void init() 15 { 16 memset(tree,0,sizeof(tree)); 17 memset(color,0,sizeof(color)); 18 memset(fail,0,sizeof(fail)); 19 memset(key,0,sizeof(key)); 20 memset(pattern,0,sizeof(pattern)); 21 cnt=1; 22 while(!q.empty()) q.pop(); 23 } 24 25 void insert(char *s) 26 { 27 int u=1,len=strlen(s); 28 for(int i=0;i<len;i++){ 29 int v=s[i]-'a'; 30 if(!tree[u][v]) tree[u][v]=++cnt; 31 u=tree[u][v]; 32 } 33 color[u]++; 34 } 35 36 void build_fail() 37 { 38 for(int i=0;i<26;i++) tree[0][i]=1; //一个非常重要的细节处理,我们加一个虚拟节点0,并将它的所有边都连到1,方便以后的运算 39 q.push(1); //将根压入队列 40 fail[1]=0; 41 while(!q.empty()){ 42 int u=q.front(); 43 q.pop(); 44 for(int i=0;i<26;i++){ //遍历所有儿子 45 if(!tree[u][i]){ //不存在该节点 46 tree[u][i]=tree[fail[u]][i]; 47 continue; 48 } 49 fail[tree[u][i]]=tree[fail[u]][i]; 50 q.push(tree[u][i]); //存在实节点才压入队列 51 } 52 } 53 } 54 55 int ac_chicken(char *ch) 56 { 57 int u=1,ans=0,len=strlen(ch); 58 for(int i=0;i<len;i++){ 59 int v=ch[i]-'a'; 60 int k=tree[u][v]; 61 while(k>1&&color[k]){ 62 ans+=color[k]; 63 color[k]=0; 64 k=fail[k]; 65 } 66 u=tree[u][v]; 67 } 68 return ans; 69 } 70 71 int main() 72 { 73 int t; 74 scanf("%d",&t); 75 while(t--){ 76 init(); 77 scanf("%d",&n); 78 for(int i=0;i<n;i++){ 79 scanf("%s",pattern); 80 insert(pattern); 81 } 82 build_fail(); 83 scanf("%s",key); 84 printf("%d\n",ac_chicken(key)); 85 } 86 return 0; 87 }