bzoj5417&&luogu4770你的名字 后缀自动机+线段树合并

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/84792273

bzoj5417&&luogu4770你的名字

题目传送门:
洛谷
bzoj

分析

题目大意:
给定一个模板串 S S ,每次给定一个字符串 T T l , r l,r ,求 T T 中有多少个本质不同的子串无法匹配 S S 的子串 S [ l r ] S[l\cdots r]
首先肯定考虑的是 l = 1 , r = S l=1,r=S 的情况。
分析 T T 的每一个前缀 T [ 1 i ] T[1\cdots i]
肯定存在一个分界点 x x ,使得这个前缀 T [ 1 i ] T[1\cdots i] 的后缀有
x j , T [ x i ] \forall x\ge j,T[x\cdots i] 可以匹配 S S
x < j , T [ x i ] \forall x < j,T[x\cdots i] 无法匹配 S S
并且这个 j j 是单调递增的,我们令 l i m i = i j + 1 lim_i=i-j+1
由于题目中要求的是本质不同的子串,所以我们必须对 T T 建立后缀自动机,对于每一个 T T 上的节点,假设其 R i g h t Right 集合中最先出现的位置为 p o s pos ,那么这个节点 v v 的贡献就是
m x [ v ] max { m x [ f a [ v ] ] , l i m [ p o s [ v ] ] } mx[v]-\max \{mx[fa[v]],lim[pos[v]]\}
这个式子的意义是,考虑后缀自动机的 p a r e n t parent 树等价于把原串的所有前缀逆序插入 T r i e Trie 后压缩, m x mx 就是这个节点到根节点的所代表的字符串长度,那么这个节点就压缩了串 m x [ v ] m x [ f a [ v ] ] mx[v]\cdots mx[fa[v]] 中的信息,而 l i m lim 是表示从某个位置往前没有贡献的最后一个位置。所以产生贡献的长度就是 m x [ v ] max { m x [ f a [ v ] ] , l i m [ p o s [ v ] ] } mx[v]-\max \{mx[fa[v]],lim[pos[v]]\}
现在考虑如何求 l i m lim
按顺序考虑 T T 的每一个前缀,由于 j j 的单调性质,可以采用类似双指针的方式,随着前缀的挪动,去找到合法的 j j
由于是匹配 S S 的子串,所以对 S S 另外建立一颗后缀自动机,我们只需要在 S S 上进行匹配。如果新走了某字符 c c ,如果存在 T r a n s ( n o w , c ) Trans(now,c) ,那么就往下走,否则的话就移动 j j ,跳 f a i l fail 链即可。

现在考虑加上 l , r l,r 的限制。
实际上等价于当前节点 n o w now R i g h t Right 集合内存在某个位置 p [ l + i j + 1 , r ] p\in[l+i-j+1,r]
因为你要从当前位置 p p 匹配长度为 i j + 1 i-j+1 的串,也就是 l i m lim
接下来我们只要求查询 S S 后缀自动机上某个节点 u u R i g h t Right 集合中是否含有在某个区间内的数。
这个东西采用线段树合并或者主席树维护子树 D f s Dfs 即可。
因为某个节点的 R i g h t Right 集合就是其 p a r e n t parent 树上儿子的 R i g h t Right 集合的并。

代码

采用线段树合并

#include<bits/stdc++.h>
#define re(x) std::memset(x, 0, sizeof(x))
const int N = 1e6 + 10;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int ps[N], rt[N], c[N], sa[N], lim[N], n;
struct Segment {
    int ls[N * 20], rs[N * 20], top;
    void Modify(int &p, int L, int R, int x) {
        p = ++top; if(L == R) return ; int m = L + R >> 1;
        x <= m ? Modify(ls[p], L, m, x) : Modify(rs[p], m + 1, R, x);
    }
    int Merge(int u, int v) {
        if(!u || !v) return u | v;
        int np = ++top;
        ls[np] = Merge(ls[u], ls[v]);
        rs[np] = Merge(rs[u], rs[v]);
        return np;
    }
    bool Que(int p, int L, int R, int st, int ed) {
        if(!p || st > ed) return false;
        if(L == st && ed == R) return true;
        int m = L + R >> 1; bool r = false;
        if(st <= m) r |= Que(ls[p], L, m, st, std::min(ed, m));
        if(ed > m) r |= Que(rs[p], m + 1, R, std::max(m + 1, st), ed);
        return r;
    }
}seg;
struct SAM {
    int ch[N][26], mx[N], fa[N], last, top;
    void Init() {re(ch[1]); last = top = 1;}
    int New(int x) {return mx[++top] = x, re(ch[top]), fa[top] = 0, top;}
    void Extend(int c, int x, bool e) {
        int p = last, np = last = New(mx[p] + 1); if(e) ps[np] = x;
        for(;p && !ch[p][c]; p = fa[p]) ch[p][c] = np;
        if(!p) fa[np] = 1;
        else {
            int q = ch[p][c];
            if(mx[q] == mx[p] + 1) fa[np] = q;
            else {
                int nq = New(mx[p] + 1);
                memcpy(ch[nq], ch[q], sizeof(ch[nq]));
                fa[nq] = fa[q]; if(e) ps[nq] = x;
                fa[q] = fa[np] = nq;
                for(;ch[p][c] == q; p = fa[p]) ch[p][c] = nq;
            }
        }
        if(!e) seg.Modify(rt[np], 1, n, x);
    }
    void Work() {
        for(int i = 1;i <= top; ++i) ++c[mx[i]];
        for(int i = 1;i <= n; ++i) c[i] += c[i - 1];
        for(int i = 1;i <= top; ++i) sa[c[mx[i]]--] = i;
        for(int i = top; i; --i)
            rt[fa[sa[i]]] = seg.Merge(rt[fa[sa[i]]], rt[sa[i]]);
    }
}S, T;
void rc(int *s, int &n, SAM &S, bool e) {
    S.Init(); char c = getchar(); for(;c < 'a' || c > 'z'; c = getchar()) ; n = 0;
    for(;c >= 'a' && c <= 'z'; c = getchar()) s[++n] = c - 'a'; 
    for(int i = 1;i <= n; ++i) S.Extend(s[i], i, e);
}
int s[N];
int main() {
    rc(s, n, S, 0); S.Work();
    for(int X = ri(), m;X--;) {
        rc(s, m, T, 1); int l = ri(), r = ri(), tot = 0;
        for(int i = 1, su = 1;i <= m; ++i) {
            int c = s[i];
            for(;su && !(S.ch[su][c] && seg.Que(rt[S.ch[su][c]], 1, n, l + tot, r));) {
                if(!tot) {su = 0; break;}
                if(--tot == S.mx[S.fa[su]]) su = S.fa[su];
            }
            if(!su) su = 1, tot = 0;
            else ++tot, su = S.ch[su][c];
            lim[i] = tot;
        }
        long long Ans = 0;
        for(int i = 2;i <= T.top; ++i)
            Ans += std::max(0, T.mx[i] - std::max(T.mx[T.fa[i]], lim[ps[i]]));
        printf("%lld\n", Ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/84792273
今日推荐