【Coel.解题报告】【调试调到头疼是什么感受?】P5231 [JSOI2012]玄武密码

题前碎语

和一位高二的学长在洛谷聊了聊,得到了许多启发。
未来还要继续努力啊!

题目梗概:

题目传送门

题目背景

在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。

很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。

题目描述

经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为 \(n\) 的序列 \(s\) 来描述,序列中的元素分别是 ESWN,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的 \(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;
}
posted @ 2021-11-28 00:09  秋泉こあい  阅读(32)  评论(0编辑  收藏  举报