题目大意:
给你一个最长为1e6的字符串,再给你一个字典,问你在这个字符串里面有多少字典中国的字符串出现过。
解题思路:
AC自动机模板题。
这道题目,有两个坑点。大概是两个坑点吧。
第一个就是要被匹配的串可能会匹配字典中的字符串多次。
第二个就是字典中的字符串可能会重复,可能会被多次匹配到。
比如一个数据:
1
3
she
she
she
shesheshe
答案是3
第一次写AC自动机,感觉很神奇。AC自动机就利用一个失配指针各种各样转居然就能在O(n)内求出来。非常神奇啊。
嘛因为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)) == ' ' ) ++(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] != ' '; ++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; }