ACM 第十四天
字符串:
1、KMP算法(模式串达到1e6)
模式串达到1e4直接暴力即可。
字符串哈希
字符串Hash的种类还是有很多种的,不过在信息学竞赛中只会用到一种名为“BKDR Hash”的字符串Hash算法。
2、AC自动机
模式串1e6,子串1e4,所求串长度很小,达到50。
要学会AC自动机,我们必须知道什么是Trie,也就是字典树。Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串)。
要搞懂AC自动机,先得有模式树(字典树)Trie和KMP模式匹配算法的基础知识。ac自动机其实就是一种多模匹配算法。
AC自动机算法分为3步:解题步骤:1、建立trie树 ;2、构造失败指针(fail指针);3、模式匹配过程。
hdu 2222 AC自动机 模板
1 具体代码; 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 struct Node 6 { 7 int cnt;//是否为该单词的最后一个结点 8 Node *fail;//失败指针 9 Node *next[26];//Trie中每个结点的各个节点 10 }*queue[500005];//队列,方便用BFS构造失败指针 11 char s[1000005];//主字符串 12 char keyword[55];//需要查找的单词 13 Node *root;//头结点 14 void Init(Node *root)//每个结点的初始化 15 { 16 root->cnt=0; 17 root->fail=NULL; 18 for(int i=0;i<26;i++) 19 root->next[i]=NULL; 20 } 21 void Build_trie(char *keyword)//构建Trie树 22 { 23 Node *p,*q; 24 int i,v; 25 int len=strlen(keyword); 26 for(i=0,p=root;i<len;i++) 27 { 28 v=keyword[i]-'a'; 29 if(p->next[v]==NULL) 30 { 31 q=(struct Node *)malloc(sizeof(Node)); 32 Init(q); 33 p->next[v]=q;//结点链接 34 } 35 p=p->next[v];//指针移动到下一个结点 36 } 37 p->cnt++;//单词最后一个结点cnt++,代表一个单词 38 } 39 void Build_AC_automation(Node *root) 40 { 41 int head=0,tail=0;//队列头、尾指针 42 queue[head++]=root;//先将root入队 43 while(head!=tail) 44 { 45 Node *p=NULL; 46 Node *temp=queue[tail++];//弹出队头结点 47 for(int i=0;i<26;i++) 48 { 49 if(temp->next[i]!=NULL)//找到实际存在的字符结点 50 { //temp->next[i] 为该结点,temp为其父结点 51 if(temp==root)//若是第一层中的字符结点,则把该结点的失败指针指向root 52 temp->next[i]->fail=root; 53 else 54 { 55 //依次回溯该节点的父节点的失败指针直到某节点的next[i]与该节点相同, 56 //则把该节点的失败指针指向该next[i]节点; 57 //若回溯到 root 都没有找到,则该节点的失败指针指向 root 58 p=temp->fail;//将该结点的父结点的失败指针给p 59 while(p!=NULL) 60 { 61 if(p->next[i]!=NULL) 62 { 63 temp->next[i]->fail=p->next[i]; 64 break; 65 } 66 p=p->fail; 67 } 68 //让该结点的失败指针也指向root 69 if(p==NULL) 70 temp->next[i]->fail=root; 71 } 72 queue[head++]=temp->next[i];//每处理一个结点,都让该结点的所有孩子依次入队 73 } 74 } 75 } 76 } 77 int query(Node *root) 78 { //i为主串指针,p为模式串指针 79 int i,v,count=0; 80 Node *p=root; 81 int len=strlen(s); 82 for(i=0;i<len;i++) 83 { 84 v=s[i]-'a'; 85 //由失败指针回溯查找,判断s[i]是否存在于Trie树中 86 while(p->next[v]==NULL && p!=root) 87 p=p->fail; 88 p=p->next[v];//找到后p指针指向该结点 89 if(p==NULL)//若指针返回为空,则没有找到与之匹配的字符 90 p=root; 91 Node *temp=p;//匹配该结点后,沿其失败指针回溯,判断其它结点是否匹配 92 while(temp!=root)//匹配结束控制 93 { 94 if(temp->cnt>=0)//判断该结点是否被访问 95 { 96 count+=temp->cnt;//由于cnt初始化为 0,所以只有cnt>0时才统计了单词的个数 97 temp->cnt=-1;//标记已访问过 98 } 99 else//结点已访问,退出循环 100 break; 101 temp=temp->fail;//回溯 失败指针 继续寻找下一个满足条件的结点 102 } 103 } 104 return count; 105 } 106 int main() 107 { 108 int T,n; 109 scanf("%d",&T); 110 while(T--) 111 { 112 root=(struct Node *)malloc(sizeof(Node)); 113 Init(root); 114 scanf("%d",&n); 115 for(int i=0;i<n;i++) 116 { 117 scanf("\n%s",keyword); 118 Build_trie(keyword); 119 } 120 Build_AC_automation(root); 121 scanf("\n%s",s); 122 printf("%d\n",query(root)); 123 } 124 return 0; 125 }
next数组 模板
1 const int N = 100100; 2 int nxt[N]; 3 char s[100100]; 4 int slen, tlen; 5 void get_Next() 6 { 7 int j, k; 8 j = 0; k = -1; nxt[0] = -1; 9 tlen=strlen(s); 10 while(j < tlen) 11 if(k == -1 || s[j] == s[k]) 12 nxt[++j] = ++k; 13 else 14 k = nxt[k]; 15 16 }
参考博客:https://blog.csdn.net/liu940204/article/details/51345954
雪儿言