题目大意:
给你一个最长为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)) == '\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;
}