AC自动机算法模板

note//目前还不完善

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int kind=26;//节点中包含的子节点数

//字典树中的节点
struct node{
     node *fail;  //失败指针
     node *next[kind];//Trie每个节点的26个子节点(最多26个字母)
     int count;      //当其值为1时为最后一个节点,表示一个单词
     node():fail(NULL),count(0){
         memset(next,NULL,sizeof(next));
     }
}*q[500001];     //队列,方便用于bfs构造失败指针
char keyword[51];   //输入的单词
char str[1000001];   //模式串
int head,tail;      //队列的头尾指针


//字典树的节点插入方法
void insert(char *str,node *root){
    node *p=root;//创建一个临时指针用于树的遍历
    int i=0,index;//i:字符串的下标,index:节点索引
    //当字符串不空时
    while(str[i]){
        //获取该字符对应的索引位置
        index=str[i]-'a';
        //判断该字符是否存在,不存在则创建节点对象
        if(p->next[index]==NULL) p->next[index]=new node();
        //指针后移,下标后移
        p=p->next[index];
        ++i;
    }
    //字符串的最后一个节点标记为1
    p->count++;
}


//创建字典树的失败指针
void build_ac_automation(node *root){
    int i;
    //根节点的失败指针为空
    root->fail=NULL;
    //根节点入队列
    q[head++]=root; //(head为全局变量)
    //当队列不空时
    while(head!=tail){
        node *temp=q[tail++];
        //构造遍历节点指针
        node *p=NULL;
        //遍历当前节点的所有儿子节点
        for(i=0;i<26;i++){
            //如果其儿子节点不为空
            if(temp->next[i]!=NULL){
                //当前节点为根节点,则其儿子节点的失败指针指向根节点
                if(temp==root) temp->next[i]->fail=root;
                else{//当前节点不是根节点,则移动到其失败指针所指向的节点
                    p=temp->fail;
                    //当节点不空时
                    while(p!=NULL){
                        if(p->next[i]!=NULL){//判断当前节点的孩子节点与当前节点的失败指针相应的孩子节点不空时,则找到失败指针
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        //迭代失败指针
                        p=p->fail;
                    }
                    //如果迭代后的失败指针为空,则将其指向根节点
                    if(p==NULL) temp->next[i]->fail=root;
                }
                //将孩子节点加入队列中
                q[head++]=temp->next[i];
            }
        }
    }
}

//查找模式串中出现的单词
int query(node *root){
    //str:为模式串
    int i=0,cnt=0,index,len=strlen(str);
    //从根节点开始遍历字典树
    node *p=root;
    //当字符串不空时
    while(str[i]){
        //获取当前遍历字符的索引
        index=str[i]-'a';
        //当前字符不存在且当前节点不是根节点 则当前指针指向其根节点
        while(p->next[index]==NULL&&p!=root) p=p->fail;
        //顺着字典树进行遍历
        p=p->next[index];
        //如果当前节点为空了,则让其指向根节点
        p=(p==NULL)? root:p;
        //设置一个临时节点指向p
        node *temp=p;
        //如果该节点不指向根节点,且不是一个单词的最后一个节点  则遍历其他的分支
        while(temp!=root&&temp->count!=-1){
            cnt+=temp->count;//如果该节点标记为0,则没有形成单词 从而不计数,如果该节点标记为1则形成单词 则计数
            temp->count=-1;//节点标记为-1表示已经遍历过了
            temp=temp->fail;
        }
        //字符后移
        i++;
    }
    //返回出现的单词数目
    return cnt;
}


int main(){
     cout<<endl<<"\t\t\tAC_Automation_Learining_Program"<<endl;
     int n;//单词的个数
     head=tail=0;
     node *root=new node();
     cin>>n;
     //往字典树中插入单词
     while(n--){
        cin>>keyword;
        insert(keyword,root);
     }
     //构造失败指针
     build_ac_automation(root);
     //输入遍历文本串
     cin>>str;
     cout<<query(root)<<endl;

    return 0;
}

//改进版。。。。。。。。。。。。。。。。。。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;

//节点包含的最多子节点数目
const int kind= 26;

//字典树中的节点
struct node{
   node *fail;//失败指针
   node *next[kind];//trie每个节点的26个子节点(最多26个字母)
   int count;  //当其值为1时表示最后一个节点且表示一个单词
   node():fail(NULL),count(0){  //默认构造函数
       memset(next,NULL,sizeof(next));
   }
};  

queue<node*> Q;//方便用于bfs构造失败指针
char keyword[51];  //存储输入的单词
char str[1000001];  //输入的目标串


//字典树的节点插入方法
void insert(char *str,node *root){
     node *p=root;//创建一个临时指针用于树的遍历
     int i=0,index;//i:字符串的下标   index:记录节点索引
     //当输入的字符串不空时
     while(str[i]){
         //获取该字符对应的索引位置
         index=str[i]-'a';//索引范围从0~26
         //判断该字符是否存在,不存在则创建节点对象
         if(p->next[index]==NULL) p->next[index]=new node();
         //指针后移,下标后移
         p=p->next[index];
         ++i;
     }
     //字符串的最后一个节点标记为1
     p->count++;
}



//字典树的失败指针构建
void build_ac_automation(node *root){
    //根节点的失败指针为空
    root->fail=NULL;
    //根节点入队列
    Q.push(root);
    //当队列不空时
    while(!Q.empty()){
        
        //取队首元素 出队列
        node *temp=Q.front();
        Q.pop();

        //构造遍历节点指针
        node *p=NULL;

        //遍历当前节点的所有儿子节点
        for(int i=0;i<26;i++){
            //如果当前节点的各个儿子节点不为空
            if(temp->next[i]!=NULL){
               //当前节点为根节点则其儿子节点的失败指针指向根节点
               if(temp==root) temp->next[i]->fail=root; 
               else{  //当前节点不是根节点 则其儿子节点的失败指针指向其失败指针所指向的节点直至所对应的儿子节点存在
                   p=temp->fail;
                   //当节点不空时
                   while(p!=NULL){
                       if(p->next[i]!=NULL){ //判断当前节点的孩子节点与当前节点的失败指针指向的孩子节点相对应时 节点不空,则找到失败指针
                           temp->next[i]->fail=p->next[i];
                           break;
                       }
                       //迭代失败指针
                       p=p->fail;
                   }
                   //如果迭代后的失败指针为空,则将其指向根节点
                   if(p==NULL) temp->next[i]->fail=root;
               }
               //将孩子节点加入队列中
               Q.push(temp->next[i]);
            }
        }
    }
}


//查找模式串中出现的单词
int query(node *root){
    //str:   为目标串 cnt为单词计数 len为目标串的长度 
    int i=0,cnt=0,index,len=strlen(str);
    //从根节点开始遍历字典树
    node *p=root;
    //当字符串不空时
    while(str[i]){
        //获取当前遍历字符的索引
        index=str[i]-'a';
        //当前字符不存在且当前节点不是根节点  则将当前指针指向其失败指针所指节点
        while(p->next[index]==NULL&&p!=root) p=p->fail;
        //顺着字典树进行遍历
        p=p->next[index];
        //如果当前节点为空了,则让其指向根节点
        p=(p==NULL)? root:p;
        //设置一个临时节点指向p
        node *temp=p;
        //如果该节点不指向根节点,且不是一个单词的最后一个节点 则遍历其他的分支
        while(temp!=root&&temp->count!=-1){
            cnt+=temp->count;//如果该节点标记为0  则没有形成单词 从而不计数 ,如果该节点标记为1则形成单词 则计数
            temp->count=-1;//节点标记为-1表示已经遍历过了
            temp=temp->fail;
        }
        //字符后移
        i++;
    }
    //返回出现的单词数目
    return cnt;
}

int main(){
    cout <<endl<<"AC_Automation_Learning_Program"<<endl;
    int n;//单词的个数
    node *root=new node();
    cin>>n;
    //往字典树中插入单词
    while(n--){
        cin>>keyword;
        insert(keyword,root);
    }
    //构造失败指针
    build_ac_automation(root);
    //输入遍历文本串
    cin>>str;
    cout<<query(root)<<endl;
    
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值