程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> hdu 2222 AC自動機

hdu 2222 AC自動機

編輯:C++入門知識

/*

一道AC自動機模板題,超時次數20+,改了半天終於AC了,代碼中標記了從TLE到AC過程修改的關鍵一步,看樣子應該是數組作為函數參數傳遞時的細節問題,不過還是不明白,如有那位大神明白個中緣由,還望不吝賜教!!!

問題終於解決了,原來是在循環中重復調用 strlen( ) 函數導致了TLE,多謝各位熱心朋友的幫助!

注意:若要遍歷一個字符數組 str 時要先用一個變量記下 strlen(str) 的值,避免在循環中不停地調用 strlen( )

*/

題目大意:先給出一些單詞,然後給出一片文章,統計文章中出現的單詞數,這裡的每個單詞只統計一次。

 

[cpp]
#include"iostream"  
#include"string"  
#include"queue"  
using namespace std; 
char s[1000001],p[55] ; 
//結點結構  
struct Node 

    int cnt ; 
    Node *fail ; 
    Node *next[26] ; 
}; 
Node *root = new Node ; 
//初始化結點  
void init(Node *t) 

    memset(t->next,NULL,sizeof(t->next)) ; 
    t->cnt = 0 ; 
    t->fail = NULL ; 

//插入新單詞  
void insert(char *str) 

    int i=0,k ; 
    Node *p = root ; 
    Node *newnode ; 
    while(str[i]){ //若用 for(i=0;i<strlen(str);i++) 一定先記下 strlen(str) 的值,防止TLE  
        k = str[i] - 'a' ; 
        if(p->next[k] == NULL){ 
            newnode = new Node ; 
            init(newnode) ; 
            p->next[k] = newnode ;  
            p = newnode ; 
        } 
        else{ 
            p = p->next[k] ; 
        } 
        i++; 
    } 
    p->cnt ++; 

//確定fail指針  
void makeFail() 

    Node *front ; 
    queue<Node *>q ; 
    q.push(root) ; 
    while(!q.empty()){ 
        front = q.front() ; 
        q.pop() ; 
        for(int i = 0;i < 26;i++){ 
            if(front->next[i] != NULL){    //父結點有孩子i,則找孩子i的fail指針  
                if(front == root) 
                    front->next[i]->fail = root ;//與根結點相連的結點的fail指針都指向根結點  
                else{ 
                    Node *temp = front ; 
                    while(temp->fail != NULL){         //父結點fail指針非空  
                        if(temp->fail->next[i] != NULL){       //父結點fail指針指向的結點有孩子i  
                            front->next[i]->fail = temp->fail->next[i] ; 
                            break ; 
                        } 
                        temp = temp->fail ;//父結點向上轉移  
                    } 
                    if(temp->fail == NULL) 
                        front->next[i]->fail = root ; 
                } 
                q.push(front->next[i]) ;//找到孩子i的fail指針後將孩子i加入隊列  
            } 
        } 
    } 

//在文章中搜索單詞  
int search(char *str) 

    Node *p = root ; 
    Node *temp = NULL ; 
    int i=0,k,ans = 0 ; 
    while(str[i]){ 
        k=str[i] - 'a' ; 
        while(p != root && p->next[k] == NULL){ 
                p = p->fail ; 
        } 
        if(p->next[k] != NULL){//p記錄當前位置最長的後綴匹配,下次從該支繼續匹配  
            p = p->next[k] ; 
            temp = p ;         //用temp繼續找當前位置較短的後綴匹配  
            while(temp != root && temp->cnt != -1){ 
                ans += temp->cnt ;//注意一定是+=,而不能直接++,因為cnt可能為0  
                temp->cnt = -1 ; 
                temp = temp->fail ; 
            } 
        } 
        i++; 
    } 
    return ans ; 

//釋放內存  
void freedom(Node *p) 

    for(int i = 0;i < 26;i++){ 
        if(p->next[i] != NULL) 
            freedom(p->next[i]) ; 
    } 
    delete p ; 

int main() 

    int t,k,i,ans ; 
    scanf("%d",&t) ; 
    while(t--){ 
        init(root) ; 
        scanf("%d",&k) ; 
        getchar(); 
        while(k--){ 
            gets(p) ; 
            insert(p) ; 
        } 
        makeFail() ; 
        gets(s) ; 
        ans = search(s) ; 
        printf("%d\n",ans) ; 
        for(i = 0;i < 26;i ++){//注意root不能刪除  
            if(root->next[i] != NULL) 
                freedom(root->next[i]) ; 
        } 
    } 
    delete root ; 
    return 0 ; 

#include"iostream"
#include"string"
#include"queue"
using namespace std;
char s[1000001],p[55] ;
//結點結構
struct Node
{
 int cnt ;
 Node *fail ;
 Node *next[26] ;
};
Node *root = new Node ;
//初始化結點
void init(Node *t)
{
 memset(t->next,NULL,sizeof(t->next)) ;
 t->cnt = 0 ;
 t->fail = NULL ;
}
//插入新單詞
void insert(char *str)
{
 int i=0,k ;
 Node *p = root ;
 Node *newnode ;
 while(str[i]){ //若用 for(i=0;i<strlen(str);i++) 一定先記下 strlen(str) 的值,防止TLE
  k = str[i] - 'a' ;
  if(p->next[k] == NULL){
   newnode = new Node ;
   init(newnode) ;
   p->next[k] = newnode ;
   p = newnode ;
  }
  else{
   p = p->next[k] ;
  }
  i++;
 }
 p->cnt ++;
}
//確定fail指針
void makeFail()
{
 Node *front ;
 queue<Node *>q ;
 q.push(root) ;
 while(!q.empty()){
  front = q.front() ;
  q.pop() ;
  for(int i = 0;i < 26;i++){
   if(front->next[i] != NULL){    //父結點有孩子i,則找孩子i的fail指針
    if(front == root)
     front->next[i]->fail = root ;//與根結點相連的結點的fail指針都指向根結點
    else{
     Node *temp = front ;
     while(temp->fail != NULL){         //父結點fail指針非空
      if(temp->fail->next[i] != NULL){       //父結點fail指針指向的結點有孩子i
       front->next[i]->fail = temp->fail->next[i] ;
       break ;
      }
      temp = temp->fail ;//父結點向上轉移
     }
     if(temp->fail == NULL)
      front->next[i]->fail = root ;
    }
    q.push(front->next[i]) ;//找到孩子i的fail指針後將孩子i加入隊列
   }
  }
 }
}
//在文章中搜索單詞
int search(char *str)
{
 Node *p = root ;
 Node *temp = NULL ;
 int i=0,k,ans = 0 ;
 while(str[i]){
  k=str[i] - 'a' ;
  while(p != root && p->next[k] == NULL){
    p = p->fail ;
  }
  if(p->next[k] != NULL){//p記錄當前位置最長的後綴匹配,下次從該支繼續匹配
   p = p->next[k] ;
   temp = p ;         //用temp繼續找當前位置較短的後綴匹配
   while(temp != root && temp->cnt != -1){
    ans += temp->cnt ;//注意一定是+=,而不能直接++,因為cnt可能為0
    temp->cnt = -1 ;
    temp = temp->fail ;
   }
  }
  i++;
 }
 return ans ;
}
//釋放內存
void freedom(Node *p)
{
 for(int i = 0;i < 26;i++){
  if(p->next[i] != NULL)
   freedom(p->next[i]) ;
 }
 delete p ;
}
int main()
{
 int t,k,i,ans ;
 scanf("%d",&t) ;
 while(t--){
  init(root) ;
  scanf("%d",&k) ;
  getchar();
  while(k--){
   gets(p) ;
   insert(p) ;
  }
  makeFail() ;
  gets(s) ;
  ans = search(s) ;
  printf("%d\n",ans) ;
  for(i = 0;i < 26;i ++){//注意root不能刪除
   if(root->next[i] != NULL)
    freedom(root->next[i]) ;
  }
 }
 delete root ;
 return 0 ;
}

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved