1. 程式人生 > >bzoj5417&&luogu4770你的名字 字尾自動機+線段樹合併

bzoj5417&&luogu4770你的名字 字尾自動機+線段樹合併

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