前缀匹配
题目链接:ybt高效进阶2-5-3
题目大意
有一个大串和一些小串,问你每个小串的前缀在大串上的最长匹配长度是多少。
每个串只有 E,S,W,N 四个字符组成。
思路
由于我们看到字符串长度都很大,我们考虑不是把 AC 自动机和其他东西结合,而是把 AC 自动机进行变形。
那怎么变形呢?
我们可以枚举每个小串的每个前缀,然后询问是否在大串中出现,那就是要 O ( 1 ) O(1) O(1) 查询。
那怎么弄呢?我们考虑预处理出来。
那我们原本是查询每个小串是否在大串中出现,现在我们如果建出每个小串组成的 Trie 树,然后枚举大串的前缀,然后看在 Trie 树上的那些地方还有这个串(也就是跳 fail 边),那你找到的串都是大串的子串。
然后就好了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
struct Trie {
int son[4], fail;
bool in;
}tree[10000001];
int n, m, ans[100001], tot;
char s[10000001], c[100001][101];
queue <int> q;
int get_go(char c) {
//给每个字符给一个独特的位置,减少空间
if (c == 'E') return 0;
if (c == 'S') return 1;
if (c == 'W') return 2;
if (c == 'N') return 3;
return -1;//读入的时候这四个都不是,就要重新读入
}
void insert(int op) {
//AC 自动机插入串
int size = strlen(c[op]);
int now = 0;
for (int i = 0; i < size; i++) {
int go = get_go(c[op][i]);
if (!tree[now].son[go]) tree[now].son[go] = ++tot;
now = tree[now].son[go];
}
}
void build_fail() {
//建 Trie 树上的 fail 边
for (int i = 0; i <= 3; i++)
if (tree[0].son[i]) {
q.push(tree[0].son[i]);
tree[tree[0].son[i]].fail = 0;
}
while (!q.empty()) {
int now = q.front();
q.pop();
for (int i = 0; i <= 3; i++)
if (tree[now].son[i]) {
q.push(tree[now].son[i]);
tree[tree[now].son[i]].fail = tree[tree[now].fail].son[i];
}
else tree[now].son[i] = tree[tree[now].fail].son[i];
}
}
void add() {
//给这个大串查询
int now = 0;
for (int i = 1; i <= n; i++) {
//枚举它的前缀
int go = get_go(s[i]);
int noww = tree[now].son[go];
while (noww) {
//找到 Trie 树上所有的这个串
tree[noww].in = 1;
noww = tree[noww].fail;
}
now = tree[now].son[go];
}
}
int ask(int op) {
int size = strlen(c[op]);
int now = 0, re = 0;
for (int i = 0; i < size; i++) {
//询问每个前缀是否存在于 Trie 数中
int go = get_go(c[op][i]);
now = tree[now].son[go];
if (tree[now].in) re = i + 1;//存在,要找最长距离的
}
return re;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
s[i] = getchar();
while (get_go(s[i]) == -1) s[i] = getchar();
}
for (int i = 1; i <= m; i++) {
scanf("%s", &c[i]);
insert(i);
}
build_fail();
add();
for (int i = 1; i <= m; i++) {
printf("%d\n", ask(i));
}
return 0;
}