——这两个玩意是好久以前学习的,现在大都忘记了,重新回忆一遍。
对于一个字符串S(文本串),我们拥有几个子串(模式串),如何去求出这些子串在S中位置?
这时候就要用到KMP算法。
简要构成就不叙述了,直接讲原理:
eg:
如下两个串,上为文本串(S),下为模式串(T),我们从头开始匹配。
S:abcabdabd
T:abcabc
到了某一位发现不一样:
S:abcabdabd
T:abcabc
那么我们就将模式串右移,因为ab两个字母是重复的。
S:abcabdabd
T: abcabc
继续匹配……
这个右移的过程是通过next数组实现的,它用来确定失配后变化的位置。
求next数组的方式:
很简单,不赘述了。
1 void nextst() 2 { 3 int j=0,k=-1; 4 nexts[0]=-1; 5 while(j<s2.length()){ 6 if(k==-1||s2[j]==s2[k]){ 7 nexts[++j]=++k; 8 } 9 else k=nexts[k]; 10 } 11 }
细心发现,这就是个模式串自匹配。
剩下的没啥好说的。我懒。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+10; 4 string s1; 5 string s2; 6 int nexts[maxn]; 7 void nextst() 8 { 9 int j=0,k=-1; 10 nexts[0]=-1; 11 while(j<s2.length()){ 12 if(k==-1||s2[j]==s2[k]){ 13 nexts[++j]=++k; 14 } 15 else k=nexts[k]; 16 } 17 } 18 void kmp(){ 19 int i=0,j=0; 20 while(j<=s2.length()){ 21 if(j==s2.length()){ 22 cout<<i-s2.length()+1<<endl; 23 j=nexts[j]; 24 } 25 if(i>=s1.length()) break; 26 while(j>=0&&s1[i]!=s2[j]) j=nexts[j]; 27 i++; 28 j++; 29 } 30 } 31 int main(){ 32 cin>>s1>>s2; 33 nextst(); 34 kmp(); 35 for(int i=1;i<=s2.length();i++){ 36 cout<<nexts[i]<<" "; 37 } 38 cout<<endl; 39 return 0; 40 }
AC自动机,顾名思义,就是可以帮助你AC problem的东西借用OIWIKI上的一句话:
AC 自动机利用一个 fail 指针来辅助多模式串的匹配。
我认为这句话基本可以概括AC自动机的特点和功能,如果还有其他细节的话,就是:
AC 自动机是 以 TRIE 的结构为基础 ,结合 KMP 的思想 建立的。
我们来看啥是TRIE树:
没啥好看的,就是一棵由字符构成的树,每个节点都是一个字符,节点的儿子是其他字符,大概长这样:
(网上找的图)
这张图就是he she is hers(意义不明)
接着有个叫fail指针的东西,类似于next数组,也是用来找匹配的。
如何构建?
这里引用一下OIWIKI上讲的:
我只会写普通版的模板,找空学一下优化:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+10; 4 int n,cnt=1,rt=1; 5 struct trie{ 6 int ch[27]; 7 int end; 8 int next; 9 }zh[maxn]; 10 void ins(const string &s){ 11 int o=rt; 12 for(int i=0;i<s.length();i++){ 13 int &t = zh[o].ch[s[i]-'a']; 14 if (!t) t = ++cnt; 15 o = t; 16 } 17 zh[o].end++; 18 } 19 void failed(){ 20 queue<int> a; 21 zh[rt].next=rt; 22 a.push(rt); 23 while(!a.empty()){ 24 int t=a.front(); 25 a.pop(); 26 for(int i=0;i<26;i++){ 27 if(zh[t].ch[i]){ 28 int fat=zh[t].next; 29 while(!zh[fat].ch[i]&&fat!=rt){ 30 fat=zh[fat].next; 31 } 32 zh[zh[t].ch[i]].next=(zh[fat].ch[i]&&t!=rt)?zh[fat].ch[i]:fat; 33 a.push(zh[t].ch[i]); 34 } 35 } 36 } 37 } 38 int sear(const string &s){ 39 int o=rt,res=0; 40 for(int i=0;i<s.length();i++){ 41 if(zh[o].ch[s[i]-'a']) o=zh[o].ch[s[i]-'a']; 42 else{ 43 o=zh[o].next; //zh[o].next 44 while(!zh[o].ch[s[i]-'a']&&o!=rt){ 45 o=zh[o].next; 46 } 47 o=(zh[o].ch[s[i]-'a'])?zh[o].ch[s[i]-'a']:o; 48 } 49 for(int t=o;t!=rt&&zh[t].end!=-1;t=zh[t].next){ 50 res+=zh[t].end; 51 zh[t].end=-1; 52 } 53 } 54 return res; 55 } 56 int main(){ 57 cin>>n; 58 for(int i=1;i<=n;i++){ 59 string s; 60 cin>>s; 61 ins(s); 62 } 63 failed(); 64 string t; 65 cin>>t; 66 cout<<sear(t)<<endl; 67 return 0; 68 }
那么回忆就暂时到此结束了,以后会找空更新这个页面的。