出现次数最多的单词 AC自动机 UVA 1449 Dominating Patterns

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4195

题意:给你n个单词,还有一个长文本s,现在要你输出在文本s中出现次数最多的单词。

分析:

       直接建立AC自动机,并且val值是当前插入单词的编号。建立AC自动机后,用s文本串去匹配,每当走过val非0的节点后都使得对应单词的cnt值+1.最后一遍扫描cnt数组,然后求出max值。然后在扫描一遍输入单词,只要当前单词的cnt值==max就输出当前单词。

       注意:这里有个问题。如果有相同的单词比如aaaaaa输入的话,那么在建立Trie的时候后来的aaa覆盖了前一个aaa,直接单词cnt[i]=0cnt[i+1]=5.结果我们就只输出了一个aaa。其实我们应该输出两次aaa(在最终结果里)。这里我们应该做个map映射,将aaa始终映射到序号i+1上,cnt[i+1]的值=5=max,最后我们扫描一遍单词aaa,用过map[aaa]来获得aaa的序号为i+1,这样就可以输出2aaa了。


#include <cstdio>  
#include <cstring>  
#include <string>  
#include <algorithm>  
#include <map>  
#include <queue>  
using namespace std;  
  
const int MAXNODE = 11000;  
const int SIGMA_SIZE = 26;  
  
struct AutoMac {  
  
    int ch[MAXNODE][SIGMA_SIZE];  
    int val[MAXNODE];  
    int next[MAXNODE];  
    int last[MAXNODE];  
    int sz;  
      
    int cnt[155];  
    map<string, int> ms;  
  
    void init() {  
    sz = 1;   
    memset(ch[0], 0, sizeof(ch[0]));  
    memset(cnt, 0, sizeof(cnt));  
    ms.clear();  
    }  
      
    int idx(char c) {  
    return c - 'a';  
    }  
      
    void insert(char *str, int v) {  
    int n = strlen(str);  
    int u = 0;  
    for (int i = 0; i < n; i++) {  
        int c = idx(str[i]);  
        if (!ch[u][c]) {  
        memset(ch[sz], 0, sizeof(ch[sz]));  
        val[sz] = 0;  
        ch[u][c] = sz++;  
        }  
        u = ch[u][c];  
    }  
    val[u] = v;  
    ms[str] = v;  
    }  
  
    void getnext() {  
    queue<int> Q;  
    next[0] = 0;      
    for (int c = 0; c < SIGMA_SIZE; c++) {  
        int u = ch[0][c];  
        if (u) {next[u] = 0; Q.push(u); last[u] = 0;}  
    }  
    while (!Q.empty()) {  
        int r = Q.front(); Q.pop();  
        for (int c = 0; c < SIGMA_SIZE; c++) {  
        int u = ch[r][c];  
        if (!u) {  
            ch[r][c] = ch[next[r]][c];  
            continue;  
        }  
        Q.push(u);  
        int v = next[r];  
        while (v && !ch[v][c]) v = next[v];  
        next[u] = ch[v][c];  
        last[u] = val[next[u]] ? next[u] : last[next[u]];  
        }  
    }  
    }  
  
    void print(int i, int j) {  
    if (j) {  
        cnt[val[j]]++;  
        print(i, last[j]);  
    }  
    }  
  
    void find(char *T) {  
    int n = strlen(T);  
    int j = 0;  
    for (int i = 0; i < n; i++) {  
        int c = idx(T[i]);  
        j = ch[j][c];  
        if (val[j]) print(i, j);  
        else if (last[j]) print(i, last[j]);  
    }  
    }  
};  
  
const int N = 1000005;  
  
AutoMac gao;  
  
int n;  
char s[155][75];      
char T[N];  
  
int main() {  
    int cas = 0;  
    while (~scanf("%d", &n) && n) {  
    gao.init();  
    for (int i = 1; i <= n; i++) {  
        scanf("%s", s[i]);  
        gao.insert(s[i], i);  
    }  
    gao.getnext();  
    scanf("%s", T);  
    gao.find(T);  
    int best = -1;  
    for (int i = 1; i <= n; i++)  
        best = max(best, gao.cnt[i]);  
    printf("%d\n", best);  
    for (int i = 1; i <= n; i++)  
        if (best == gao.cnt[gao.ms[s[i]]])  
        printf("%s\n", s[i]);  
    }  
    return 0;  
}  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值