1. 程式人生 > >[Codeforces 666E]Forensic Examination(廣義字尾自動機 + 線段樹合併 + 樹上倍增)

[Codeforces 666E]Forensic Examination(廣義字尾自動機 + 線段樹合併 + 樹上倍增)

Address

Meaning

  • 給定一個字串 S S m m
    個字串 T 1 , T 2 , .
    . . , T m T_1,T_2,...,T_m
  • q
    q
    個詢問,每個詢問給出四個引數 l l r r p l p_l p r p_r
  • S S 的子串 [ p l , p r ] [p_l,p_r] T l T_l T l + 1 T_{l+1} 、…、 T r T_r 中的哪個串中出現的次數最多
  • 如果出現次數最多的有多個串則取編號最小的
  • 對於每組詢問輸出編號和出現次數
  • 1 S 5 × 1 0 5 1\le|S|\le5\times10^5 1 m 5 × 1 0 4 1\le m\le5\times10^4 1 i = 1 m T i 5 × 1 0 4 1\le\sum_{i=1}^m|T_i|\le5\times10^4 1 q 5 × 1 0 5 1\le q\le5\times10^5
  • 時限 6s ,空限 768MB

Solution

  • 首先把所有的 T 1 , T 2 , . . . , T m T_1,T_2,...,T_m S S 放在一起建立廣義 SAM ,下面就對這個 SAM 對應的 Parent 樹進行討論
  • 下面我們定義「黑點」為對 R i g h t Right 集合有貢獻的點(即 SAM 構建過程中,不是被拆解出的所有狀態點)
  • 第一個問題:對於樹上的已知節點 u u 以及已知區間 [ l , r ] [l,r] ,如何知道 T [ l . . . r ] T[l...r] 這些串中,哪個串出現狀態 u u 的次數最多(相同者取最小編號),以及出現次數
  • 根據 Parent 樹的性質,狀態 u u R i g h t Right 集合可以用 u u 的子樹內所有黑點的某些東西表示出來
  • 又根據 SAM 的性質,一個黑點對應原串的一個字首。相應地,在廣義 SAM 上,一個黑點對應原串集合的 Trie 樹上根到一個點的路徑
  • 可以在每個黑點上,用一個vector儲存這個黑點對應了字串集合 T T 中哪些串的字首
  • 以下把一個黑點上的 vector 記憶體的 T T 內的字串編號記作 [ 1 , m ] [1,m] 內的某種顏色
  • 我們的做法出來了:這個問題就是求 u u 的子樹內哪種顏色出現次數最多(相同則取最小編號顏色)以及出現次數
  • 可以使用線段樹合併或者可持久化線段樹解決這個問題
  • 對每個點開一棵線段樹,下標為顏色,每個節點存出現次數最多的顏色及出現次數,對於每個點 u u ,通過線段樹合併從 u u 的子節點的資訊合併到點 u u 的資訊
  • 到現在,我們已經解決了第一個問題
  • 第二個問題:已知區間 [ p l , p r ] [p_l,p_r] ,如何找到子串 S [ p l . . . p r ] S[p_l...p_r] 在 SAM 上對應的狀態點
  • 如果是 S [ 1... p r ] S[1...p_r] 對應的狀態點,那麼要找的點顯然是 S S 的長度為 p r p_r 的字首對應的黑點
  • 而根據 Parent 樹的性質, S [ p l . . . p r ] S[p_l...p_r] 對應的點是 S [ 1... p r ] S[1...p_r] 對應點的祖先,且每個點的父親節點的 m a x l maxl 都嚴格小於自己的 m a x l maxl
  • 於是,如果 S [ 1... p r ] S[1...p_r] 對應的狀態點為 u u ,那麼我們要做的就是找到 u u 的祖先中,離 u u 最遠的,滿足 m a x l v r l + 1 maxl_v\ge r-l+1 的點 v v
  • 可以使用樹上倍增找到這個點 v v
  • 這樣我們的做法就出來了:先通過線段樹合併預處理 Parent 樹每個點的子樹內資訊,詢問時通過樹上倍增找到 S [ p l . . . p r ] S[p_l...p_r] 對應的狀態 u