You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
2.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 10010 * 55; //模式串最长长度,短串
const int M = 1e6 + 10; //长度为m的文章长串
int n;
//tr:trie树每个结点最多26个儿子
//cnt:trie的每个结点存在的以此结点结尾的字符串个数
//idx:游标变量,结点号
int tr[N][26], cnt[N], idx;
int st[N]; //是不是统计过了,统计过的不用重复统计
string str; //字符串变量,前面用做输入模式串,最后一个是文章串
//AC自动机自己的数据结构 q:bfs用的队列 fail:失配指针
int q[N], fail[N];
//构建Trie树
void insert() {
int p = 0; //游标变量
for (int i = 0; i < str.size(); i++) { //遍历模式字符串
int t = str[i] - 'a'; //计算对应的索引号
if (!tr[p][t]) tr[p][t] = ++idx; //如果不存在这个结点,则创建之
p = tr[p][t]; //走进去
}
cnt[p]++; //打上字符串完结标识
}
//构建AC自动机(填充失配指针)
void build() {
int hh = 0, tt = -1;//清空队列所以前面不需要每次都memset清空q
for (int i = 0; i < 26; i++) //查找root的所有26个可能存在的儿子
if (tr[0][i]) //如果该儿子存在
q[++tt] = tr[0][i]; //把这个儿子放入队列中
while (hh <= tt) { //bfs框架
int p = q[hh++]; //取出队列头,父结点
for (int i = 0; i < 26; i++) { //尝试找出该结点的所有儿子
int c = tr[p][i]; //子结点
//如果不存在,这个的失配指针指向,父节点的失配指针对应的相同字节点下
if (!c) tr[p][i] = tr[fail[p]][i];
else {
//存在依旧指向父节点的失配指针下的同子节点
fail[c] = tr[fail[p]][i];
q[++tt] = c;//放入队列
}
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> str;
//构建Trie树
insert();
}
//构建AC自动机
build();
//读入文章长串
cin >> str;
int res = 0;
//j记录当前树节点的指针初始是根节点
for (int i = 0, j = 0; i < str.size(); i++) { //枚举总串str的每一个字母
int u = str[i] - 'a';
j = tr[j][u]; //跳到下一个树节点
int p = j; //每次从当前树节点开始
while (p && !st[p]) {
res += cnt[p]; //累加命中的模式串个数
cnt[p] = 0; //去除标记
p = fail[p]; //继续查询
st[p] = 1; //标识已统计过
}
}
printf("%d\n", res);
return 0;
}