[BZOJ2746][HEOI2012]旅行问题(AC自动机+LCA)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=2746

Solution

先介绍下 fail 树:
将 AC 自动机上每个点 u u 的 fail 指针指向的点作为 u 的父亲,构成的树就是 fail 树。
应用:如果 fail 树上 u u v 的祖先,那么 u u 代表的字符串是 v 代表的字符串的前缀兼后缀。
而题意就是询问两个前缀的一个最长的公共后缀 S S <script type="math/tex" id="MathJax-Element-602">S</script> ,满足 S 是给定串之一的前缀
结合 fail 树的性质和定义分析,可以发现此题可以转化为求 fail 树上两点的 LCA 。
套用 LCA 的模板即可通过此题。

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e6 + 5, PYZ = 1e9 + 7, LogN = 22, E = 26;
int n, m, QAQ, len, que[N];
char s[N];
struct cyx {
    int son[E], dep, fa[LogN], code;
    void init() {
        dep = code = 0;
        memset(son, 0, sizeof(son));
        memset(fa, 0, sizeof(fa));
    }
} T[N];
vector<int> sp[N];
void ins(int id) {
    int i, u = 1, n = strlen(s + 1);
    For (i, 1, n) {
        int c = s[i] - 'a';
        if (!T[u].son[c]) T[T[u].son[c] = ++QAQ].init();
        T[T[u].son[c]].code = (26ll * T[u].code % PYZ + c) % PYZ;
        u = T[u].son[c]; sp[id].push_back(u);
    }
}
void cyxisdalao() {
    int i, c; T[0].init();
    For (c, 0, 25) T[0].son[c] = 1;
    que[len = 1] = 1; For (i, 1, len) {
        int u = que[i]; For (c, 0, 25)
            if (!T[u].son[c]) T[u].son[c] = T[T[u].fa[0]].son[c];
            else T[T[u].son[c]].fa[0] = T[T[u].fa[0]].son[c],
                T[T[u].son[c]].dep = T[T[T[u].son[c]].fa[0]].dep + 1,
                que[++len] = T[u].son[c];
        For (c, 0, 19) T[u].fa[c + 1] = T[T[u].fa[c]].fa[c];
    }
}
int lca(int u, int v) {
    int i; if (T[u].dep < T[v].dep) swap(u, v);
    Rof (i, 20, 0) {
        if (T[T[u].fa[i]].dep >= T[v].dep) u = T[u].fa[i];
        if (u == v) return u;
    }
    Rof (i, 20, 0) if (T[u].fa[i] != T[v].fa[i])
        u = T[u].fa[i], v = T[v].fa[i];
    return T[u].fa[0];
}
int main() {
    T[QAQ = 1].init(); T[1].dep = 1;
    int i, x, y, a, b; n = read();
    For (i, 1, n) scanf("%s", s + 1), ins(i);
    cyxisdalao(); m = read(); while (m--) {
        x = read(); y = read(); a = read(); b = read();
        printf("%d\n", T[lca(sp[x][y - 1], sp[a][b - 1])].code);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值