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.

106 lines
4.4 KiB

2 years ago
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e5 + 10; //模式串的个数最多是1e5个
const int M = 1e7 + 10; //母串的最长长度不会超过1e7
char s[N][110]; // 模式串的长度最长不超过100
char T[M]; // 母串
int tr[M][4], idx; // AC自动机需要的Trie树idx:点的索引号
int n, m; // 母串的长度n,m个模式串
bool st[M]; // st[i]:标识i结点是不是母串可以到达的位置
//这种字符向数字进行映射的问题最好的办法就是if,其它的什么Hash化都是渣渣~
int get(char x) {
if (x == 'N') return 0;
if (x == 'E') return 1;
if (x == 'S') return 2;
if (x == 'W') return 3;
}
void insert(char *s, int x) {
int p = 0;
for (int i = 0; s[i]; i++) {
int t = get(s[i]); //这里有映射关系与标准模板是有差别的 int t = s[i] - 'a';
if (!tr[p][t]) tr[p][t] = ++idx;
p = tr[p][t];
}
}
//构建AC自动机也就是在维护失配指针。这里与标准模板一模一样
int q[M], ne[M]; //这里使用M是因为模式串个数是1e5个每个长度是100所以总共的基于模式串创建Trie树节点总个数是1e5*100=1e7恰好与M一样而已
void bfs() {
int hh = 0, tt = -1;
for (int i = 0; i < 4; i++)
if (tr[0][i]) q[++tt] = tr[0][i];
while (hh <= tt) {
int p = q[hh++];
for (int i = 0; i < 4; i++) {
int t = tr[p][i];
if (!t)
tr[p][i] = tr[ne[p]][i];
else {
ne[t] = tr[ne[p]][i];
q[++tt] = t;
}
}
}
}
//在通过模式串构建的Trie图上跑一遍母串目的是标识母串走了哪些点这些点就是每个模式串的最长前缀所到达的位置
//在query操作中将对于母串到达的每个位置进行标记
void query(char *s) {
int p = 0; //从根开始,根节点在模板中设置为0
for (int i = 0; s[i]; i++) { //枚举母串的每个字符,看看母串都可以到达哪些 Trie树上的节点
p = tr[p][get(s[i])]; //从根开始通过输入的字符映射到相应的数字0~3,找到它可以到达的点
for (int j = p; j; j = ne[j]) { // p点可以到达那么一旦下一个位置失配时ne[p]也可以到达的,因为它们有共同的前缀?
//另外还有一个小小的优化,如果说对于一个节点它已经被标记过了,那么它的$ne$链上的所有点一定也被标记了,所以这时就可以直接$break$。
if (st[j]) break; // 这句加上,性能会好一点; 不加也一样能AC。
st[j] = true; // 标识j点是母串可以到达的点
}
}
}
int main() {
//加快读入
ios::sync_with_stdio(false), cin.tie(0);
//第一行有两个整数,分别表示母串的长度 n 和文字段的个数 m
cin >> n >> m;
//第二行有一个长度为 n 的字符串,表示母串 T
//如果是输入一串字符串并保存到字符数组中,系统会自动在后面补\0无需自己输入。
//也就是不需要知道到底这个字符串的真实长度n无用
cin >> T;
// m个模式串
for (int i = 1; i <= m; i++) {
cin >> s[i];
/*
使s[]s
100
*/
insert(s[i], i);
}
//构建AC自动机
bfs();
//上母串,标记母串所有可以匹配到的位置
query(T);
//对于每一个模式串,我们就从它的$Trie$树上进行检索,当发现第一个没有被母串匹配到的结点时,当前长度就是最大的匹配长度
for (int i = 1; i <= m; i++) { //枚举每个模式串
int res = 0, p = 0;
for (int j = 0; s[i][j]; j++) { //枚举每个模式串的所有前缀
p = tr[p][get(s[i][j])]; //沿着Trie前进
if (st[p]) res = j + 1; //如果标识过此位置那么结果更新为最长前缀的长度j+1
}
printf("%d\n", res); //输出最长长缀长度
}
return 0;
}