题意
给定N个模本串, 1个匹配串, 求这N个模本串在匹配串中出现的次数
题解
以后看到这种多模匹配的题目, 直接上 AC自动机, 第一次接触这个, 傻傻的跑了N遍KMP果断超时…
研究了一番ac自动机, 发现其实就是在在字典树里跑KMP, 通过fail指针来处理失配时的走向, 最终统计出个数
一篇非常详细的讲ac自动机的博客
kuangbin大神的AC自动机总结
代码
类封装模板
#include <bits/stdc++.h>
using namespace std;
#define rg register
#define sc scanf
#define pf printf
class Aho_Corasick {
public:
static const int MAXN = 1e4*50+10;
int nxt[MAXN][26], fail[MAXN], end[MAXN];
int root, L;
Aho_Corasick ( ) { };
int newnode ( ) {
for ( int i = 0; i < 26; i++ )
nxt[L][i] = -1;
end[L++] = 0;
return L-1;
}
void init ( ) { // 先初始化
L = 0;
root = newnode ( );
}
void insert ( char buf[] ) { // 再插入多个模式串构成字典树
int len = strlen ( buf );
int now = root;
for ( int i = 0; i < len; i++ ) {
if ( nxt[now][buf[i] - 'a'] == -1 )
nxt[now][buf[i] - 'a'] = newnode ( );
now = nxt[now][buf[i] - 'a'];
}
end[now]++;
}
void build ( ) { // 然后给树建立失配指针
queue<int>Q;
fail[root] = root;
for ( int i = 0; i < 26; i++ )
if ( nxt[root][i] == -1 )
nxt[root][i] = root;
else {
fail[nxt[root][i]] = root;
Q.push ( nxt[root][i] );
}
while ( !Q.empty ( ) ) {
int now = Q.front ( );
Q.pop ( );
for ( int i = 0; i < 26; i++ )
if ( nxt[now][i] == -1 )
nxt[now][i] = nxt[fail[now]][i];
else {
fail[nxt[now][i]] = nxt[fail[now]][i];
Q.push ( nxt[now][i] );
}
}
}
long long query ( char buf[] ) { // 查询模式串在文本串出现次数前需要先构建 build 构建适配指针
int len = strlen ( buf ), now = root; long long res = 0;
for ( int i = 0; i < len; i++ ) {
now = nxt[now][buf[i] - 'a'];
int temp = now;
while ( temp != root ) {
res += end[temp];
end[temp] = 0;
temp = fail[temp];
}
}
return res;
}
}ac;
char buf[1000010];
int main ( ) {
int T;
int n;
sc ( "%d", &T );
while ( T-- ) {
sc ( "%d", &n );
ac.init ( );
for ( int i = 0; i < n; i++ ) {
sc ( "%s", buf );
ac.insert ( buf );
}
ac.build ( );
sc ( "%s", buf );
pf ( "%d\n", ac.query ( buf ) );
}
return 0;
}
// 给定N个模式串, 1个 求这N个模本串在匹配串中出现的次数
void debug ( ) {
for ( int i = 0; i < L; i++ ) {
pf ( "id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i] );
for ( int j = 0; j < 26; j++ )
pf ( "%2d", nxt[i][j] );
pf ( "]\n" );
}
}
单纯版本
#include <bits/stdc++.h>
using namespace std;
#define rg register
#define sc scanf
#define pf printf
const int MAXN = 1e4*50+10;
struct Trie {
int nxt[MAXN][26], fail[MAXN], end[MAXN];
int root, L;
int newnode ( ) {
for ( int i = 0; i < 26; i++ )
nxt[L][i] = -1;
end[L++] = 0;
return L - 1;
}
void init ( ) {
L = 0;
root = newnode ( );
}
void insert ( char buf[] ) {
int len = strlen ( buf );
int now = root;
for ( int i = 0; i < len; i++ ) {
if ( nxt[now][buf[i] - 'a'] == -1 )
nxt[now][buf[i] - 'a'] = newnode ( );
now = nxt[now][buf[i] - 'a'];
}
end[now]++;
}
void build ( ) {
queue<int>Q;
fail[root] = root;
for ( int i = 0; i < 26; i++ )
if ( nxt[root][i] == -1 )
nxt[root][i] = root;
else {
fail[nxt[root][i]] = root;
Q.push ( nxt[root][i] );
}
while ( !Q.empty ( ) ) {
int now = Q.front ( );
Q.pop ( );
for ( int i = 0; i < 26; i++ )
if ( nxt[now][i] == -1 )
nxt[now][i] = nxt[fail[now]][i];
else {
fail[nxt[now][i]] = nxt[fail[now]][i];
Q.push ( nxt[now][i] );
}
}
}
int query ( char buf[] ) {
int len = strlen ( buf );
int now = root;
int res = 0;
for ( int i = 0; i < len; i++ ) {
now = nxt[now][buf[i] - 'a'];
int temp = now;
while ( temp != root ) {
res += end[temp];
end[temp] = 0;
temp = fail[temp];
}
}
return res;
}
void debug ( ) {
for ( int i = 0; i < L; i++ ) {
pf ( "id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i] );
for ( int j = 0; j < 26; j++ )
pf ( "%2d", nxt[i][j] );
pf ( "]\n" );
}
}
};
char buf[1000010];
Trie ac;
int main ( ) {
int T;
int n;
sc ( "%d", &T );
while ( T-- ) {
sc ( "%d", &n );
ac.init ( );
for ( int i = 0; i < n; i++ ) {
sc ( "%s", buf );
ac.insert ( buf );
}
ac.build ( );
sc ( "%s", buf );
pf ( "%d\n", ac.query ( buf ) );
}
return 0;
}
// 给定N个模式串, 1个 求这N个模本串在匹配串中出现的次数