AC自动机

来源:互联网 发布:生产环境 调试java 编辑:程序博客网 时间:2024/05/29 08:16
/** * 定义Trie树中子节点的最大个数,26个英文字母 * */const int MAX_NUM=26;/** * 用于构造Fail指针的队列,队列元素为拥有三个数据域 * 的对象: * fail表示Fail指针 * Child数组表示26个子节点指针 * IsOver表示当前节点是否为一个单词的结束节点 * */struct Node {        Node *fail;        Node *Child[MAX_NUM];        int IsOver;        Node() {                fail=NULL;                IsOver=0;                for(int i=0;i<26;i++)                        Child[i]=NULL;        }} *queue[1000];/** * pattern表示输入的单词 * target表示目标匹配文本 * */char pattern[100];char target[10000];/** * head表示队列queue中的头结点,新元素入队的位置 * tail表示队列queue中的尾节点,元素出队的位置 * */int head;int tail;/** * 首先利用pattern构建Trie树 * Trie树的根节点root是一个包含空字符的辅助节点; * pointer指针遍历遍历Trie树 * temp指针临时替换一个节点的26个子节点中的一个 * index指针索引一个pattern中的字符 * 如果有多个pattern字符串,则需要多次调用此方法 * */void ConstructTrieTree(char *pattern, Node *root) {        Node *pointer=root;        Node *temp;        char *index=pattern;        /**         * 此处循环以需要插入Trie树的pattern字符串作为依据         * */        while(*index!='\0') {                temp=pointer->Child[*index-'a'];                /**                 * 如果某一个字符对应的指针为NULL,则创建对应的节点                 * */                if(temp==NULL)                        temp=new Node();                pointer=temp;                index++;        }        /**         * 当index指针pattern末尾的时候循环结束;         * 此时pointer指向pattern的最后一个字符所在的节点         * */        pointer->IsOver=1;}/** * 然后构造Fail指针: * 在Trie树上构建Fail指针的基本策略如同BFS遍历 * 初始化将root入队,然后进入循环,循环检查队列是否为空,如果非空,则 * 取出一个节点进行处理,并将该的所节点有子节点加入队列;如果队列为空, * 则算法结束; * 对节点进行处理就是寻找其Fail指针的指向位置 * 如果当前节点的字符为A,则跳转到当前节点的父节点的fail指针指向的节点 * 处,判断其儿子中是否有为A字符的节点,如果没有则继续按照其自身的fail * 指针指向的节点跳转,直到找到字符为A的节点或者到达root节点处 * */void ConstructFailPointer(Node *root, Node **queue) {        /**         * 初始化root节点的fail指针,并将其入队queue         * */        head=0;tail=0;        root->fail=NULL;        queue[head++]=root;        Node *temp;        Node *index;        while(head!=tail) {                /**                 * 从队列末尾获取一个节点                 * */                temp=queue[tail++];                index=NULL;                for(int i=0;i<26;i++) {                        /**                         * 处理当前节点,遍历其Child指针数组                         * */                        if(temp->Child[i]!=NULL) {                                /**                                 * 对于root节点的直接子节点而言,由于他们没有                                 * 任何前缀而言,所以其fail指针都指向root节点                                 * */                                if(temp==root)                                        temp->Child[i]->fail=root;                                else {                                        index=temp->fail;                                        while(index!=NULL) {                                                /**                                                 * 循环访问当前节点的fail指针指向的节点                                                 * 考察其子节点中的Child[i]是否存在,也就是                                                 * 是否有相同的字符                                                 * */                                                if(index->Child[i]!=NULL) {                                                        /**                                                         * Child[i]表示当前处理的字符子节点                                                         * temp表示原始父亲节点                                                         * index表示根据fail指针跳转的节点                                                         * */                                                        temp->Child[i]->fail=index->Child[i];                                                        break;                                                }                                                index=index->fail;                                        }                                        /**                                         * root节点的fail指针初始化为NULL                                         * */                                        if(index==NULL)                                                temp->Child[i]->fail=root;                                }                                /**                                 * 将temp节点的子节点入队queue                                 * */                                queue[head++]=temp->Child[i];                        }                }        }}/** * 最后正式施行字符串匹配操作: * 利用Trie树(已经将所有的Pattern串插入)与target文本串进行匹配,有两种情况: * 1. 如果某一个节点的Child[i]对应target的字符存在,则下到子节点 * 2. 如果某一个节点的所有子节点Child[i]都没有与target的字符匹配的,则使用fail跳转 * 匹配函数最终返回pattern数组中单词在target文本串中出现的个数 * */int MultiPatternSearch(Node *root, char *target) {        char *index1=target;        Node *index2=root;        Node *temp;        int i=0, count=0;        while(index1[i]!='\0') {                /**                 * 如果不能匹配则跳转到fail指针指向的节点                 * */                while(index2->Child[index1[i]-'a']==NULL && index2!=root)                        index2=index2->fail;                /**                 * 准备匹配下一个字符,但如果上述循环是因为后面一个条件结束,则index2                 * 需要重新指向root                 * */                index2=index2->Child[index1[i]-'a'];                if(index2==NULL)                        index2=root;                temp=index2;                while(temp!=root && temp->IsOver==1) {                        count++;                        /**                         * 如果Trie树中的一个pattern成功匹配,则需要                         * 将其IsOver域设置成0,防止重复匹配;                         * 然后跳转到与其具有公共前缀的fail指针指向的节点                         * 继续进行匹配                         * */                        temp->IsOver=0;                        temp=temp->fail;                }                i++;        }        return count;}