Dominating Patterns UVA-1449 AC自动机模板题

OJ:https://vjudge.net/problem/UVA-1449

简单翻译:

给定n个模式串和一个文本串T,只包含小写字母,你的任务是找出哪些模式串在T中出现的次数最多,输出对多的次数和出现最多的那个模式串。如果有多个次数相同的模式串,按输入顺序输出多个模式串。

 

这种多模但是长度比较短的提直接应该就能想到是AC自动机, 该题也就是一个非常明显的模板题,只需要很小的变动,因为我们要统计出现的次数,一个模式串可能会出现多次,且题中可能也会给出多个相同的模式串,最后还要输出出现次数最多的那个串,需要进行一点点的特殊处理。我们在trie的节点结构体上增加了一个index变量,用其来记录当前节点是哪个模式串的结尾,

这样我们在匹配到当前节点之后,就能给对应的模式串出现次数进行增加操作。

 

代码实现:我觉得代码如果看不懂的话,一定是不懂AC自动机,网上讲解很多,懂了的话,这个题可以作为练手题

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 1000005
#define QUEUE_SIZE 70*26
#define max(x,y) x > y ? x : y
typedef struct{
    // 当前是不是一个模式串的结束位置
    int val;
    int word[26];
    // 失配节点
    int fail;
    // 当前节点如果是一个模式串的结束位置,index就用来记录当前是哪个模式串
    int index;
}TrieNode;

typedef struct{
    TrieNode nodes[71*150];
    // 当前trie上的节点数量
    int size;
}Trie;

int idx(char c);
void insert(char *s,int index);
void buildAc();
int isEmpty();
void push(int data);
int pop();
Trie trie;

// 存放模式串
char pattern[151][71];
// T串
char T[MAX];
// 队列相关,队列都要自己写,真惨
int queue[QUEUE_SIZE],pre=0,next=0,size=0;
int main()
{
    int n = 0;
    for(;1;){
        trie.size = 1;
        memset(trie.nodes[0].word,0,sizeof(trie.nodes[0].word));
        pre = 0,next = 0,size= 0;
        scanf("%d",&n);
        if(n == 0){
            return 0;
        }
        for(int i=0;i<n;i++){
            scanf("%s",pattern[i]);
            insert(pattern[i],i);
        }
        // 记录每个模式串出现的次数
        int count[n];
        memset(count,0,sizeof(count));
        buildAc();
        scanf("%s",T);
        int len = strlen(T);
        int curNode = 0;
        for(int i=0;i<len;i++){
            int nextNode = trie.nodes[curNode].word[idx(T[i])];
            if(nextNode){
                if(trie.nodes[nextNode].val){
                    count[trie.nodes[nextNode].index]++;
                }
                curNode = nextNode;
            }else{
                if(curNode!=0){
                    curNode = trie.nodes[curNode].fail;
                    i--;
                }
            }
        }
        int res = -1;
        for(int i=0;i<n;i++){
            res = max(res,count[i]);
        }
        printf("%d\n",res);
        for(int i=0;i<n;i++){
            if(count[i] == res){
                printf("%s\n",pattern[i]);
            }
        }
    }
    return 0;
}

void push(int data){
    size++;
    queue[next++] = data;
    if(next == QUEUE_SIZE){
        next = 0;
    }
}

int pop(){
    size--;
    int res = queue[pre++];
    if(pre == QUEUE_SIZE){
        pre = 0;
    }
    return res;
}

int isEmpty(){
    return size == 0;
}

void buildAc(){
    for(int i=0;i<26;i++){
        if(trie.nodes->word[i]){
            int index = trie.nodes->word[i];
            push(index);
            trie.nodes[index].fail = 0;
        }
    }
    while(!isEmpty()){
        int parentNode = pop();
        for(int i=0;i<26;i++){
            int curNode = trie.nodes[parentNode].word[i];
            if(curNode){
                push(curNode);
                int pFail = trie.nodes[parentNode].fail;
                while(pFail && !trie.nodes[pFail].word[i]){
                    pFail = trie.nodes[pFail].fail;
                }
                trie.nodes[curNode].fail = trie.nodes[pFail].word[i];
            }
        }
    }

}

void insert(char *s,int index){
    int curNode = 0,len = strlen(s);
    for(int i=0;i<len;i++){
        int index = idx(s[i]);
        if(!trie.nodes[curNode].word[index]){
            memset(trie.nodes[trie.size].word,0,sizeof(trie.nodes[0].word));
            trie.nodes[trie.size].val = 0;
            trie.nodes[curNode].word[index] = trie.size++;
        }
        curNode = trie.nodes[curNode].word[index];
    }
    trie.nodes[curNode].val += 1;
    trie.nodes[curNode].index = index;
}

int idx(char c){
    return c - 'a';
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值