hdu 1277 AC自動機入門(指標版和陣列版)

weixin_30924079發表於2020-04-04

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=1277

推薦一篇部落格(看思路就可以,實現用的是java):

https://www.cnblogs.com/nullzx/p/7499397.html

這是一道模板題,拿來練手,之前看了一篇部落格,有點錯誤,但是hdu上面居然過了,最主要的是我在hdu上面三道AC自動機模板題都是這個錯的程式碼,居然都過了,害的我糾結了一晚上,原來是資料太水了。

主要還是看上面的部落格,寫了點註釋,不一定對,以後好拿來複習。

程式碼(指標版):

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 10005
char str[maxn][62];
char s[62];
struct node{
    int id;//記錄當前關鍵字的編號 
    node *next[10];//指向兒子 
    node *fail;//失配後指向的地方,相當於KMP裡面的next陣列的作用 
    node(){
        id=-1;
        memset(next,0,sizeof(next));
        fail=NULL;
    }
};
queue<int>qe;//記錄出現的關鍵字編號 
queue<node*>q;//BFS 
int n,m,k,t;
void insert(char *s,node *root,int ID){//插入關鍵字到trie裡面 
    node *p=root;
    int len=strlen(s);
    for(int i=0;i<len;i++){
        int id=s[i]-'0';
        if(p->next[id]==NULL)
        p->next[id]=new node();
        p=p->next[id];
    }
    p->id=ID;//在關鍵字結束的地方把id更新為關鍵字的ID 
}
void build_fail(node *root){//構建fail指標,要和KMP演算法聯想起來作比較 
    while(!q.empty())
    q.pop(); 
    q.push(root);
    while(!q.empty()){
        node *temp=q.front();
        q.pop();
        for(int i=0;i<10;i++){
            if(temp->next[i]==NULL)
            continue;
            if(temp==root)//讓root節點的所有兒子的fail指標都指向root,相當於next[0]=-1 
            temp->next[i]->fail=root;
            else{
                node *p=temp->fail;//把temp->fail賦給p,因為接下來p要改變 
                while(p!=NULL){//一直向fail方向走,直到找到某個點的next[i]!=NULL或者p==NULL(找不到) 
                    if(p->next[i]!=NULL){
                        temp->next[i]->fail=p->next[i];
                        break;
                    }
                    p=p->fail;
                }
                if(p==NULL)//如果找不到,就讓temp的兒子i的fail指標指向根 
                temp->next[i]->fail=root;
            }
            q.push(temp->next[i]);
        }
    }
}
void query(node *root){
    while(!qe.empty())
    qe.pop();
    node *temp=root;
    for(int i=0;i<n;i++){
        for(int j=0;j<60;j++){
            int id=str[i][j]-'0';
            //一直往fail指向的方向找,直到找到或者到了root,相當於KMP裡面K一直等於next[K],最後匹配成功或者K=-1 
            while(temp->next[id]==NULL&&temp!=root) 
            temp=temp->fail;
            temp=temp->next[id];//temp下移
            if(temp==NULL)
            temp=root;
            node *p=temp;
            while(p!=root){//在所有的以i+'0'字元結尾的字首裡面找以這個字元結束的單詞 
                if(p->id!=-1){//注意這個p->id的位置,剛剛學,看了錯的部落格,把它寫在了上面,hdu居然過了,糾結了我老半天 
                    qe.push(p->id);
                    p->id=-1;//因為可能會有多個字尾和某一個字首相同,可能會重複計數,所以我們要標記一下 
                }
                p=p->fail;
            }
        }
    }
}
void destroy(node *root){
    if(root==NULL)
    return;
    for(int i=0;i<10;i++){
        if(root->next[i]!=NULL)
        destroy(root->next[i]);
    }
    delete root;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        node *root=new node();
        for(int i=0;i<n;i++)
        scanf("%s",str[i]);
        getchar();
        char c;
        for(int i=0;i<m;i++){
            int num=0;//計算空格數量,一旦達到了三個就說明接下來開始輸入關鍵字了 
            while(num!=3&&(c=getchar())){
                if(c==' ')
                num++;
            }
            scanf("%s",s);//輸入關鍵字 
            insert(s,root,i);//把關鍵字插入字典樹 
        }
        build_fail(root);//構建fail指標 
        query(root);//掃描 
        if(qe.size()==0)
        printf("No key can be found !\n");
        else{
            printf("Found key:");
            while(!qe.empty()){
                printf(" [Key No. %d]",qe.front()+1);
                qe.pop();
            }
            printf("\n");
        }
        destroy(root);//釋放記憶體 
    }
    return 0;
}

 程式碼(陣列版):

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 600005
int trie[maxn][10],fail[maxn],val[maxn],ID[maxn];
char str[1005][65],s[65];
int n,m,t,cnt;
queue<int>q;
void init(){
    memset(trie,0,sizeof(trie));
    memset(fail,0,sizeof(fail));//讓所有點的fail都指向0(根節點) 
    memset(val,0,sizeof(val));
    cnt=0;
}
void insert(char *s,int k){
    int root=0;
    for(int i=0;s[i];i++){
        int id=s[i]-'0';
        if(trie[root][id]==0)
        trie[root][id]=++cnt;
        root=trie[root][id];
    }
    val[root]++;//標記單詞結尾 
    ID[root]=k;//記錄ID 
} 
void build_fail(){//構建fail指標 
    while(!q.empty())
    q.pop();
    int root=0;
    for(int i=0;i<10;i++){//把root的兒子都壓入佇列 
        if(trie[root][i])
        q.push(trie[root][i]);
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<10;i++){
            if(trie[u][i]){//如果u的兒子i存在,然它兒子的fail指向fail[u]的兒子,並壓入佇列 
                fail[trie[u][i]]=trie[fail[u]][i];
                q.push(trie[u][i]);
            }else{//如果不存在,把fail[u]的兒子i變成u的兒子 
                trie[u][i]=trie[fail[u]][i];
            }
        }
    }
}
void query(){
    while(!q.empty())
    q.pop();
    int u=0;
    for(int i=0;i<n;i++){
        for(int j=0;j<60;j++){
            int id=str[i][j]-'0';
            u=trie[u][id];//u在trie樹上往下移 
            int temp=u;//臨時儲存u 
            while(temp!=0){//遍歷temp和它的fail指向的所有以str[i][j]結尾的字首,看裡面有多少個是單詞結尾,這裡可以加一個&&val[temp]!=0優化一下
                if(val[temp]){//如果是單詞結尾 
                    q.push(ID[temp]);//壓進佇列 
                    val[temp]=0;//把標記去掉,防止重複計算 
                }
                temp=fail[temp];//往fail方向走,直到回到根節點 
            }
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        for(int i=0;i<n;i++){
            scanf("%s",str[i]);
        }
        getchar();
        for(int i=0;i<m;i++){
            int num=0;
            char c;
            while(num!=3&&(c=getchar())){
                if(c==' '){
                    num++;
                    continue;
                }
            }
            scanf("%s",s);
            insert(s,i);
        }
        build_fail();
        query();
        if(q.size()==0)
        printf("No key can be found !\n");
        else{
            printf("Found key:");
            while(!q.empty()){
                printf(" [Key No. %d]",q.front()+1);
                q.pop();
            }
            printf("\n");
        }
    }
    return 0;
}

 

轉載於:https://www.cnblogs.com/6262369sss/p/10298380.html

相關文章