【Coel.解题报告】【调试调到头疼是什么感受?】P5231 [JSOI2012]玄武密码
题前碎语
和一位高二的学长在洛谷聊了聊,得到了许多启发。
未来还要继续努力啊!
题目梗概:
题目背景
在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。
很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。
题目描述
经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为 \(n\) 的序列 \(s\) 来描述,序列中的元素分别是 E
,S
,W
,N
,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的 \(m\) 段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。
现在,考古工作者遇到了一个难题。对于每一段文字 \(t\),求出其最长的前缀 \(p\),满足 \(p\) 是 \(s\) 的子串。
输入格式
第一行有两个整数,分别表示母串的长度 \(n\) 和文字段的个数 \(m\)。
第二行有一个长度为 \(n\) 的字符串,表示母串 \(s\)。
接下来 \(m\) 行,每行一个字符串,表示一段带有玄武密码的文字 \(t\)。
输出格式
对于每段文字,输出一行一个整数,表示最长的 \(p\) 的长度。
解题思路
不得不说江苏的文化底蕴还是挺高的,玄武湖,进香河,鸡笼山,神秘的玄武神灵……
什么时候广西能有这些东西就好了(想peach
看到多模式串匹配,自然会想到用AC自动机解决。
但是要求最长串,暴力枚举肯定是不行的,怎么办呢?
考虑利用失配函数的性质,对主串进行标记,再放到各个模式串里匹配,遇到标记就跳,可以大大提高效率。
代码如下:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#define maxn 10000050
#define maxt 105
#define maxm 100050
using namespace std;
int tot, n, m, trie[maxn][4], fail[maxn];
char s[maxn], t[maxm][maxt];
bool vis[maxn];
queue<int> Q;
inline int trans(char c) {
if (c == 'E')
return 0;
if (c == 'S')
return 1;
if (c == 'W')
return 2;
if (c == 'N')
return 3;
}
inline void insert(char* s) {
int len = strlen(s), u = 0;
for (register int i = 0; i < len; i++) {
int c = trans(s[i]);
if (!trie[u][c])
trie[u][c] = ++tot;
u = trie[u][c];
}
}
inline void getfail() {
for (register int i = 0; i < 4; i++) {
int u = trie[0][i];
if (u)
Q.push(u);
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (register int i = 0; i < 4; i++) {
int c = trie[u][i];
if (c) {
fail[c] = trie[fail[u]][i];
Q.push(c);
} else
trie[u][i] = trie[fail[u]][i];
}
}
}
inline void target() {
int len = strlen(s), u = 0;
for (register int i = 0; i < len; i++) {
int c = trans(s[i]);
u = trie[u][c];
for (int j = u; j; j = fail[j])
vis[j] = 1;
}
}
inline int query(char* s) {
int len = strlen(s), u = 0, ans = 0;
for (register int i = 0; i < len; i++) {
int c = trans(s[i]);
u = trie[u][c];
if (vis[u])
ans = i + 1;
else
break;
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
scanf("%s", s);
for (register int i = 1; i <= m; i++) {
scanf("%s", t[i]);
insert(t[i]);
}
getfail();
target();
for (register int i = 1; i <= m; i++)
printf("%d\n", query(t[i]));
return 0;
}