Luogu4770 NOI2018 你的名字

本文详细解析了Luogu4770题目,一种高效的子串匹配算法。通过构建后缀自动机和使用线段树进行优化,解决了给定字符串的子串是否为另一字符串子串的问题,适用于大规模数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面luogu4770

题意:给定一个串 S S S。每次询问给出一个字符串 T T T,问 T T T有多少个不同的子串使得其也不是 S [ l … r ] S[l … r] S[lr]的子串。 ∣ S ∣ ≤ 5 × 1 0 5 |S| \le 5 \times 10^5 S5×105 q ≤ 1 0 5 q \le 10^5 q105 ∑ ∣ T ∣ ≤ 1 0 6 \sum |T| \le 10^6 T106

题解:首先考虑部分分 l = 1 , r = ∣ S ∣ l = 1, r = |S| l=1,r=S的做法:对 S S S T T T建后缀自动机。对 T T T的每个位置求出以这个位置为右端点,向左最多匹配 S S S的长度 m x mx mx。那么对于 T T T后缀自动机每个节点,其贡献的答案为 l e n [ v ] − m a x ( l e n [ l i n k [ v ] ] , m x ) len[v] - max (len[link[v]], mx) len[v]max(len[link[v]],mx)

考虑 l , r l, r l,r任意的情况。对于每个 S S S的每个节点开棵线段树维护 e n d p o s endpos endpos。线段树合并。

一样在 S S S的后缀自动机上匹配。现在需要询问后缀自动机上当前节点的所有 e n d p o s endpos endpos能否将该串放入 [ l , r ] [l, r] [l,r]

即询问 [ l + l e n − 1 , r ] [l + len - 1, r] [l+len1,r]有没有合法的 e n d p o s endpos endpos。在线段树上查询即可。

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
typedef long long ll ;
const int maxn = 1e6 + 10 ;
int n ;
char s[maxn] ;
int mx[maxn], rt[maxn], c[maxn], p[maxn] ;
vector<int> E[maxn] ;
struct segmentTree {
    int tot, ch[maxn * 20][2] ;
    void insert (int &now, int pos, int l, int r) {
        now = ++ tot ;
        if (l == r) return ;
        int m = (l + r) >> 1 ;
        if (pos <= m) insert (ch[now][0], pos, l, m) ;
        else insert (ch[now][1], pos, m + 1, r) ;
    }
    int merge (int x, int y) {
        if (!x || !y) return x + y ;
        int now = ++ tot ;
        ch[now][0] = merge (ch[x][0], ch[y][0]) ;
        ch[now][1] = merge (ch[x][1], ch[y][1]) ;
        return now ;
    }
    bool query (int x, int L, int R, int l, int r) {
        if (!x) return 0 ;
        if (L <= l && R >= r) return 1 ;
        int m = (l + r) >> 1, res = 0 ;
        if (L <= m) res |= query (ch[x][0], L, R, l, m) ;
        if (R > m) res |= query (ch[x][1], L, R, m + 1, r) ;
        return res ;
    }
}tree ;
struct suffixAutomaton {
    int sz, last ;
    struct node {
        int len, link, pos, nxt[26] ;
    }st[maxn] ;
    void init () {
        for (int i = 1; i <= sz; i ++) {
            st[i].len = st[i].link = st[i].pos = 0 ;
            memset (st[i].nxt, 0, sizeof st[i].nxt) ;
        }
        sz = last = 1 ;
    }
    void insert (int c, int id) {
        int cur = ++ sz; st[cur].pos = id ;
        st[cur].len = st[last].len + 1 ;
        int p = last ;
        while (p && !st[p].nxt[c]) {
            st[p].nxt[c] = cur; p = st[p].link ;
        }
        if (!p) {
            st[cur].link = 1; last = cur; return ;
        }
        int q = st[p].nxt[c] ;
        if (st[p].len + 1 == st[q].len) {
            st[cur].link = q ;
        } else {
            int clone = ++ sz ;
            st[clone] = st[q] ;
            st[clone].len = st[p].len + 1 ;
            while (p && st[p].nxt[c] == q) {
                st[p].nxt[c] = clone; p = st[p].link ;
            }
            st[cur].link = st[q].link = clone ;
        }
        last = cur ;
    }
}A, B ;
void dfs (int v) {
    for (int i = 0; i < E[v].size(); i ++) {
        int to = E[v][i] ;
        dfs (to) ;
        rt[v] = tree.merge (rt[v], rt[to]) ;
    }
}
bool check (int u, int len, int l, int r, int c) {
    if (l + len > r) return 0 ;
    if (!A.st[u].nxt[c]) return 0 ;
    u = A.st[u].nxt[c] ;
    return tree.query (rt[u], l + len, r, 1, n) ;
}
int main() {
    scanf("%s", s + 1) ;
    n = strlen (s + 1) ;
    A.init () ;
    for (int i = 1; i <= n; i ++) {
        A.insert (s[i] - 'a', i) ;
        tree.insert (rt[A.last], i, 1, n) ;
    }
    for (int i = 1; i <= A.sz; i ++) E[A.st[i].link].push_back (i) ;
    dfs (1) ;
    int q, l, r ;
    scanf("%d", &q) ;
    while (q --) {
        scanf("%s", s + 1) ;
        int l = read(), r = read() ;
        int len = strlen (s + 1) ;
        B.init () ;
        int u = 1, ln = 0 ;
        for (int i = 1; i <= len; i ++) {
            while (u && !check (u, ln, l, r, s[i] - 'a')) {
                if (-- ln <= A.st[A.st[u].link].len)
                    u = A.st[u].link ;
            }
            if (!u) u = 1, ln = 0 ;
            else u = A.st[u].nxt[s[i] - 'a'], ln ++ ;
            B.insert (s[i] - 'a', i) ;
            mx[i] = ln ;
        }
        ll ans = 0 ;
        for (int i = 2; i <= B.sz; i ++) ans += max (0, B.st[i].len - max (mx[B.st[i].pos], B.st[B.st[i].link].len)) ;
        printf("%lld\n", ans) ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值