AC自動機及其模板
阿新 • • 發佈:2017-10-01
closed i++ auto ota color namespace 構建 malloc 模板題
模板
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
const int Max_Tot = 5e5 + 10;
const int Max_Len = 1e6 + 10;
const int Letter = 26;
struct Aho{
struct StateTable{
int Next[Letter];
int fail, cnt;
}Node[Max_Tot];
int Size;
queue <int> que;
inline void init(){
while(!que.empty()) que.pop();
memset(Node[0].Next, 0, sizeof(Node[0].Next));
Node[0].fail = Node[0].cnt = 0;
Size = 1;
}
inline void insert(char *s){
int len = strlen(s);
int now = 0;
for(int i=0; i<len; i++){
int idx = s[i] - ‘a‘;
if(!Node[now].Next[idx]){
memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
Node[Size].fail = Node[Size].cnt = 0;
Node[now].Next[idx] = Size++;
}
now = Node[now].Next[idx];
}
Node[now].cnt ++;
}
inline void BuildFail(){
Node[0].fail = -1;
que.push(0);
while(!que.empty()){
int top = que.front(); que.pop();
for(int i=0; i<Letter; i++){
if(Node[top].Next[i]){
if(top == 0) Node[ Node[top].Next[i] ].fail = 0;
else{
int v = Node[top].fail;
while(v != -1){
if(Node[v].Next[i]){
Node[ Node[top].Next[i] ].fail = Node[v].Next[i];
break;
}v = Node[v].fail;
}if(v == -1) Node[ Node[top].Next[i] ].fail = 0;
}que.push(Node[top].Next[i]);
}
}
}
}
inline void Get(int u, int &res){
while(u){
res += Node[u].cnt;
Node[u].cnt = 0;
u = Node[u].fail;
}
}
int Match(char *s){
int len = strlen(s);
int res = 0, now = 0;
for(int i=0; i<len; i++){
int idx = s[i] - ‘a‘;
if(Node[now].Next[idx]) now = Node[now].Next[idx];
else{
int p = Node[now].fail;
while(p!=-1 && Node[p].Next[idx]==0) p = Node[p].fail;
if(p == -1) now = 0;
else now = Node[p].Next[idx];
}
if(Node[now].cnt) Get(now, res);
}
return res;
}
}ac;
char S[Max_Len];
int main(void)
{
// ac.init();
// ac.BuildFail();
// ac.Match();
// .....
return 0;
}
View Code
#include<bits/stdc++.h>
using namespace std;
#define MAX_N 1000006 /// 主串長度
#define MAX_Tot 500005 /// 字典樹上可能的最多的結點數 = Max串數 * Max串長
struct Aho{
struct state{
int next[26];
int fail,cnt;
}st[MAX_Tot]; /// 節點結構體
int Size; /// 節點個數
queue<int> que;/// BFS構建fail指針的隊列
void init(){
while(que.size())que.pop();/// 清空隊列
for(int i=0;i<MAX_Tot;i++){/// 初始化節點,有時候 MLE 的時候,可以嘗試將此初始化放到要操作的時候再來初始化
memset(st[i].next,0,sizeof(st[i].next));
st[i].fail=st[i].cnt=0;
}
Size=1;/// 本來就有一個空的根節點
}
void insert(char *S){/// 插入模式串
int len=strlen(S);/// 復雜度為O(n),所以別寫進for循環
int now=0;/// 當前結點是哪一個,從0即根開始
for(int i=0;i<len;i++){
char c = S[i];
if(!st[now].next[c-‘a‘]) st[now].next[c-‘a‘]=Size++;
now=st[now].next[c-‘a‘];
}
st[now].cnt++;/// 給這個串末尾打上標記
}
void build(){/// 構建 fail 指針
st[0].fail=-1;/// 根節點的 fail 指向自己
que.push(0);/// 將根節點入隊
while(que.size()){
int top = que.front(); que.pop();
for(int i=0; i<26; i++){
if(st[top].next[i]){/// 如果當前節點有 i 這個兒子
if(top == 0) st[st[top].next[i]].fail=0;/// 第二層節點 fail 應全指向根
else {
int v = st[top].fail;/// 走向 top 節點父親的 fail 指針指向的地方,嘗試找一個最長前綴
while(v != -1){/// 如果走到 -1 則說明回到根了
if(st[v].next[i]){/// 如果有一個最長前綴後面接著的也是 i 這個字符,則說明 top->next[i] 的 fail 指針可以指向這裏
st[st[top].next[i]].fail = st[v].next[i];
break;/// break 保證找到的前綴是最長的
}
v = st[v].fail;/// 否則繼續往父親的父親的 fail 跳,即後綴在變短( KMP 思想 )
} if(v == -1) st[st[top].next[i]].fail=0;/// 如果從頭到尾都沒找到,那麽就只能指向根了
} que.push(st[top].next[i]);/// 將這個節點入隊,為了下面建立 fail 節點做準備
}
}
}
}
int get(int u){
int res = 0;
while(u){
res = res + st[u].cnt;
st[u].cnt = 0;
u = st[u].fail;
}
return res;
}
int match(char *S){
int len = strlen(S);/// 主串長度
int res=0,now=0;/// 主串能夠和多少個模式串匹配的結果、當前的節點是哪一個
for(int i=0; i<len; i++){
char c = S[i];
if(st[now].next[c-‘a‘]) now=st[now].next[c-‘a‘];/// 如果匹配了,則不用跳到 fail 處,直接往下一個字符匹配
else {
int p = st[now].fail;
while( p!=-1 && st[p].next[c-‘a‘]==0 ) p=st[p].fail;/// 跳到 fail 指針處去匹配 c-‘a‘ ,直到跳到 -1 也就是沒得跳的時候
if(p == -1) now = 0;/// 如果全部都不匹配,只能回到根節點了
else now = st[p].next[c-‘a‘];/// 否則當前節點就是到達了能夠匹配的 fail 指針指向處
}
if(st[now].cnt)/// 如果當前節點是個字符串的結尾,這個時候就能統計其對於答案貢獻了,答案的貢獻應該是它自己 + 它所有 fail 指針指向的節點
/// 實際也就是它匹配了,那麽它的 fail 指向的前綴以及 fail 的 fail 實際上也應該是匹配了,所以循環跳 fail 直到無法再跳為止
res = res + get(now);
}
return res;
}
}ac;
int T;
int N;
char S[MAX_N];
int main(){
// ac.init();
// ac.build();
// ac.match();
// ...
return 0;
}
帶註釋
#include <stdio.h> #include <stdlib.h> #include <string.h> struct Node { int cnt;//是否為該單詞的最後一個結點 Node *fail;//失敗指針 Node *next[26];//Trie中每個結點的各個節點 }*queue[500005];//隊列,方便用BFS構造失敗指針 char s[1000005];//主字符串 char keyword[55];//需要查找的單詞 Node *root;//頭結點 void Init(Node *root)//每個結點的初始化 { root->cnt=0; root->fail=NULL; for(int i=0;i<26;i++) root->next[i]=NULL; } void Build_trie(char *keyword)//構建Trie樹 { Node *p,*q; int i,v; int len=strlen(keyword); for(i=0,p=root;i<len;i++) { v=keyword[i]-‘a‘; if(p->next[v]==NULL) { q=(struct Node *)malloc(sizeof(Node)); Init(q); p->next[v]=q;//結點鏈接 } p=p->next[v];//指針移動到下一個結點 } p->cnt++;//單詞最後一個結點cnt++,代表一個單詞 } void Build_AC_automation(Node *root) { int head=0,tail=0;//隊列頭、尾指針 queue[head++]=root;//先將root入隊 while(head!=tail) { Node *p=NULL; Node *temp=queue[tail++];//彈出隊頭結點 for(int i=0;i<26;i++) { if(temp->next[i]!=NULL)//找到實際存在的字符結點 { //temp->next[i] 為該結點,temp為其父結點 if(temp==root)//若是第一層中的字符結點,則把該結點的失敗指針指向root temp->next[i]->fail=root; else { //依次回溯該節點的父節點的失敗指針直到某節點的next[i]與該節點相同, //則把該節點的失敗指針指向該next[i]節點; //若回溯到 root 都沒有找到,則該節點的失敗指針指向 root p=temp->fail;//將該結點的父結點的失敗指針給p while(p!=NULL) { if(p->next[i]!=NULL) { temp->next[i]->fail=p->next[i]; break; } p=p->fail; } //讓該結點的失敗指針也指向root if(p==NULL) temp->next[i]->fail=root; } queue[head++]=temp->next[i];//每處理一個結點,都讓該結點的所有孩子依次入隊 } } } } int query(Node *root) { //i為主串指針,p為模式串指針 int i,v,count=0; Node *p=root; int len=strlen(s); for(i=0;i<len;i++) { v=s[i]-‘a‘; //由失敗指針回溯查找,判斷s[i]是否存在於Trie樹中 while(p->next[v]==NULL && p!=root) p=p->fail; p=p->next[v];//找到後p指針指向該結點 if(p==NULL)//若指針返回為空,則沒有找到與之匹配的字符 p=root; Node *temp=p;//匹配該結點後,沿其失敗指針回溯,判斷其它結點是否匹配 while(temp!=root)//匹配結束控制 { if(temp->cnt>=0)//判斷該結點是否被訪問 { count+=temp->cnt;//由於cnt初始化為 0,所以只有cnt>0時才統計了單詞的個數 temp->cnt=-1;//標記已訪問過 } else//結點已訪問,退出循環 break; temp=temp->fail;//回溯 失敗指針 繼續尋找下一個滿足條件的結點 } } return count; } int main() { int T,n; scanf("%d",&T); while(T--) { root=(struct Node *)malloc(sizeof(Node)); Init(root); scanf("%d",&n); for(int i=0;i<n;i++) { scanf("\n%s",keyword); Build_trie(keyword); } Build_AC_automation(root); scanf("\n%s",s); printf("%d\n",query(root)); } return 0; }指針版
參考博客
http://blog.csdn.net/niushuai666/article/details/7002823
http://blog.csdn.net/silence401/article/details/52662605
http://blog.csdn.net/liu940204/article/details/51345954
http://blog.csdn.net/creatorx/article/details/71100840
相關題目
HDU 2222
題意 : 給出 n 個模式串再給出一個主串,問你有多少個模式串曾在這個主串上出現過
分析 : 模板題,註意每一次計數完成後要將 cnt 的值置為 0 以免重復計算
#include<bits/stdc++.h>
using namespace std;
#define MAX_N 1000006 /// 主串長度
#define MAX_Tot 500005 /// 字典樹上可能的最多的結點數 = Max串數 * Max串長
struct Aho{
struct state{
int next[26];
int fail,cnt;
}st[MAX_Tot]; /// 節點結構體
int Size; /// 節點個數
queue<int> que;/// BFS構建fail指針的隊列
void init(){
while(que.size())que.pop();/// 清空隊列
for(int i=0;i<MAX_Tot;i++){/// 初始化節點,有時候 MLE 的時候,可以嘗試將此初始化放到要操作的時候再來初始化
memset(st[i].next,0,sizeof(st[i].next));
st[i].fail=st[i].cnt=0;
}
Size=1;/// 本來就有一個空的根節點
}
void insert(char *S){/// 插入模式串
int len=strlen(S);/// 復雜度為O(n),所以別寫進for循環
int now=0;/// 當前結點是哪一個,從0即根開始
for(int i=0;i<len;i++){
char c = S[i];
if(!st[now].next[c-‘a‘]) st[now].next[c-‘a‘]=Size++;
now=st[now].next[c-‘a‘];
}
st[now].cnt++;/// 給這個串末尾打上標記
}
void build(){/// 構建 fail 指針
st[0].fail=-1;/// 根節點的 fail 指向自己
que.push(0);/// 將根節點入隊
while(que.size()){
int top = que.front(); que.pop();
for(int i=0; i<26; i++){
if(st[top].next[i]){/// 如果當前節點有 i 這個兒子
if(top == 0) st[st[top].next[i]].fail=0;/// 第二層節點 fail 應全指向根
else {
int v = st[top].fail;/// 走向 top 節點父親的 fail 指針指向的地方,嘗試找一個最長前綴
while(v != -1){/// 如果走到 -1 則說明回到根了
if(st[v].next[i]){/// 如果有一個最長前綴後面接著的也是 i 這個字符,則說明 top->next[i] 的 fail 指針可以指向這裏
st[st[top].next[i]].fail = st[v].next[i];
break;/// break 保證找到的前綴是最長的
}
v = st[v].fail;/// 否則繼續往父親的父親的 fail 跳,即後綴在變短( KMP 思想 )
} if(v == -1) st[st[top].next[i]].fail=0;/// 如果從頭到尾都沒找到,那麽就只能指向根了
} que.push(st[top].next[i]);/// 將這個節點入隊,為了下面建立 fail 節點做準備
}
}
}
}
int get(int u){
int res = 0;
while(u){
res = res + st[u].cnt;
st[u].cnt = 0;
u = st[u].fail;
}
return res;
}
int match(char *S){
int len = strlen(S);/// 主串長度
int res=0,now=0;/// 主串能夠和多少個模式串匹配的結果、當前的節點是哪一個
for(int i=0; i<len; i++){
char c = S[i];
if(st[now].next[c-‘a‘]) now=st[now].next[c-‘a‘];/// 如果匹配了,則不用跳到 fail 處,直接往下一個字符匹配
else {
int p = st[now].fail;
while( p!=-1 && st[p].next[c-‘a‘]==0 ) p=st[p].fail;/// 跳到 fail 指針處去匹配 c-‘a‘ ,直到跳到 -1 也就是沒得跳的時候
if(p == -1) now = 0;/// 如果全部都不匹配,只能回到根節點了
else now = st[p].next[c-‘a‘];/// 否則當前節點就是到達了能夠匹配的 fail 指針指向處
}
if(st[now].cnt)/// 如果當前節點是個字符串的結尾,這個時候就能統計其對於答案貢獻了,答案的貢獻應該是它自己 + 它所有 fail 指針指向的節點
/// 實際也就是它匹配了,那麽它的 fail 指向的前綴以及 fail 的 fail 實際上也應該是匹配了,所以循環跳 fail 直到無法再跳為止
res = res + get(now);
}
return res;
}
}aho;
int T;
int N;
char S[MAX_N];
int main(){
scanf("%d",&T);
while(T--){
aho.init();
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%s",S);
aho.insert(S);
}
aho.build();
scanf("%s",S);
printf("%d\n",aho.match(S));
}
return 0;
}
View Code
HDU 2896
題意 : 中文就不贅述了……
分析 : 模板題,可見的ascii碼範圍的話,直接開到128即可
#include<queue>
#include<stdio.h>
#include<string.h>
using namespace std;
const int Max_Tot = 1e5 + 10;
const int Max_Len = 1e4 + 10;
const int Letter = 128;
struct Aho{
struct StateTable{
int Next[Letter];
int fail, id;
}Node[Max_Tot];
int Size;
queue<int> que;
inline void init(){
while(!que.empty()) que.pop();
memset(Node[0].Next, 0, sizeof(Node[0].Next));
Node[0].fail = Node[0].id = 0;
Size = 1;
}
inline void insert(char *s, const int id){
int len = strlen(s);
int now = 0;
for(int i=0; i<len; i++){
int idx = s[i];
if(!Node[now].Next[idx]){
memset(Node[Size].Next, 0, sizeof(Node[Size].Next));
Node[Size].fail = Node[Size].id = 0;
Node[now].Next[idx] = Size++;
}
now = Node[now].Next[idx];
}
Node[now].id = id;
}
inline void BuildFail(){
Node[0].fail = -1;
que.push(0);
while(!que.empty()){
int top = que.front(); que.pop();
for(int i=0; i<Letter; i++){
if(Node[top].Next[i]){
if(top == 0) Node[ Node[top].Next[i] ].fail = 0;
else{
int v = Node[top].fail;
while(v != -1){
if(Node[v].Next[i]){
Node[ Node[top].Next[i] ].fail = Node[v].Next[i];
break;
}v = Node[v].fail;
}if(v == -1) Node[ Node[top].Next[i] ].fail = 0;
}que.push(Node[top].Next[i]);
}
}
}
}
inline void Get(int u, bool *used){
while(u){
if(!used[Node[u].id] && Node[u].id)
used[Node[u].id] = true;
u = Node[u].fail;
}
}
bool Match(char *s, bool *used){
int now = 0;
bool ok = false;
for(int i=0; s[i]; i++){
int idx = s[i];
if(Node[now].Next[idx]) now = Node[now].Next[idx];
else{
int p = Node[now].fail;
while(p!=-1 && Node[p].Next[idx]==0){
p = Node[p].fail;
}
if(p == -1) now = 0;
else now = Node[p].Next[idx];
}
if(Node[now].id) { Get(now, used); ok = true; }
}
if(ok) return true;
return false;
}
}ac;
char S[Max_Len];
bool used[501];
int main(void)
{
int n, m;
memset(used, false, sizeof(used));
while(~scanf("%d", &n)){
ac.init();
for(int i=1; i<=n; i++){
scanf("%s", S);
ac.insert(S, i);
}
ac.BuildFail();
int ans = 0;
scanf("%d", &m);
for(int i=1; i<=m; i++){
scanf("%s", S);
if(ac.Match(S, used)){
printf("web %d:", i);
for(int j=1; j<=n; j++){
if(used[j]){
printf(" %d", j);
used[j] = false;
}
}puts("");
ans++;
}
}
printf("total: %d\n", ans);
}
return 0;
}
View Code
AC自動機及其模板