HDU-2222 Keywords Search

题目大意:

给你一个最长为1e6的字符串,再给你一个字典,问你在这个字符串里面有多少字典中国的字符串出现过。


解题思路:

AC自动机模板题。

这道题目,有两个坑点。大概是两个坑点吧。

第一个就是要被匹配的串可能会匹配字典中的字符串多次。

第二个就是字典中的字符串可能会重复,可能会被多次匹配到。

比如一个数据:

1

3

she

she

she

shesheshe

答案是3


第一次写AC自动机,感觉很神奇。AC自动机就利用一个失配指针各种各样转居然就能在O(n)内求出来。非常神奇啊。

嘛因为AC自动机我也是刚学会。所以推荐两份资料:

Aho-Corasick自动机浅析

AC自动机算法


代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 26;
const int maxm = 1e6 + 5;

typedef struct node{
    int num;
    node *fail;
    node *ch[maxn];
    node() {
        num = 0;
        fail = NULL;
        for( int i = 0; i < maxn; ++i )
            ch[i] = NULL;
    }
}Trie;

Trie *root;
char str[maxm], s[55];

void Insert( char *s ) {
    Trie *p = root;
    for( char *c = s; *c != 0; ++c ) {
        int i = (*c) - 'a';
        if( p -> ch[i] == NULL )
            p -> ch[i] = new Trie;
        p = p -> ch[i];
        if( (*(c + 1)) == '\0' ) ++(p -> num);
    }
}

void buildFailPointer() {
    queue<Trie *> q;
    while( !q.empty() ) q.pop();

    q.push( root );
    while( !q.empty() ) {
        Trie *now = q.front(); q.pop();
        for( int i = 0; i < maxn; ++i ) {
            if( now -> ch[i] != NULL ) {
                if( now == root ) now -> ch[i] -> fail = root;
                else {
                    Trie *p = now -> fail;
                    while( p != NULL ) {
                        if( p -> ch[i] != NULL ) {
                            now -> ch[i] -> fail = p -> ch[i];
                            break;
                        }
                        p = p -> fail;
                    }
                    if( p == NULL ) now -> ch[i] -> fail = root;
                }
                q.push( now -> ch[i] );
            }
        }
    }
}
int AC_auto() {
    int ans = 0;
    Trie *p = root;
    for( int i = 0; str[i] != '\0'; ++i ) {
        int k = str[i] - 'a';
        while( p -> ch[k] == NULL && p != root  ) p = p -> fail;
        if( p -> ch[k] == NULL ) continue;
        p = p -> ch[k];

        Trie *t = p;
        while( t != root ) {
            if( t -> num != 0 ) {
                ans += t -> num;
                t -> num = 0;
            }
            t = t -> fail;
        }
    }
    return ans;
}
void clearTrie( Trie *p ) {
    for( int i = 0; i < maxn; ++i ) {
        if( p -> ch[i] != NULL )
            clearTrie( p -> ch[i] );
    }
    delete p;
}
int main() {
    ios::sync_with_stdio(false);

    int n, t; cin >> t;
    while( t-- ) {
        cin >> n;
        root = new Trie;

        for( int i = 0; i < n; ++i ) {
            cin >> s;
            Insert(s);
        }
        buildFailPointer();
        cin >> str;
        cout << AC_auto() << endl;
        clearTrie( root );
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值