CF 666E Forensic Examination 【SAM 倍增 線段樹合併】
阿新 • • 發佈:2020-11-16
題意:
給出一個串\(s\)和\(n\)個串\(t_i\),\(q\)次詢問,每次詢問串\(s\)的子串\(s[p_l:p_r]\)在串\(t_l\)到\(t_r\)中哪個串中出現次數最多,以及出現次數最多的哪個串的下標
題解:
考慮把\(n\)個\(t\)串建出廣義字尾自動機,然後字尾自動機上每個節點用動態開點線段樹來維護每個\(t\)串能匹配到的數量,把每個\(t\)串的每個字尾能匹配的最長的串對應的字尾自動機上的點以當前\(t\)串的下標在當前點的線段樹上加一,然後做線段樹合併
對於串\(s\),記錄以每個位置為右端點的能在自動機上匹配的最長子串長度,以及在自動機上對應的點,用類似做\(LCS\)
對於每次詢問,找到自動機上對應\(s[p_l:p_r]\)的節點,這個可以從以\(s[p_r]\)為右端點的最長匹配串對應的自動機上的點不斷跳\(parent\)樹找到,可以用倍增來優化,然後直接查詢區間最大值和最大值下標即可
要注意\(s[p_l:p_r]\)在自動機上無匹配點的情況,特判一下即可
view code
//#pragma GCC optimize("O3") //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace std; function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);}; const int MAXN = 5e5+7; struct SegmentTree{ int tot, ls[MAXN<<5], rs[MAXN<<5], root[MAXN]; pair<int,int> maxx[MAXN<<5]; SegmentTree(){ memset(root,0,sizeof(root)); tot = 0; } int newnode(){ tot++; ls[tot] = rs[tot] = 0; maxx[tot] = make_pair(0,0); return tot; } void pushup(int rt){ if(maxx[ls[rt]].first>=maxx[rs[rt]].first) maxx[rt] = maxx[ls[rt]]; else maxx[rt] = maxx[rs[rt]]; } void modify(int &rt, int pos, int L, int R, int x){ if(!rt) rt = newnode(); if(L + 1 == R){ maxx[rt].first += x; maxx[rt].second = L; return; } int mid = (L + R) >> 1; if(pos < mid) modify(ls[rt],pos,L,mid,x); else modify(rs[rt],pos,mid,R,x); pushup(rt); } pair<int,int> ask(int L, int R, int l, int r, int rt){ if(!rt or l>=R or L>=r) return make_pair(0,MAXN); if(L<=l and r<=R) return maxx[rt]; int mid = (l + r) >> 1; auto p1 = ask(L,R,l,mid,ls[rt]); auto p2 = ask(L,R,mid,r,rs[rt]); if(p1.first>=p2.first) return p1; else return p2; } int merge(int u, int v, int L, int R){ if(!u or !v) return u | v; if(L + 1 == R){ maxx[u].first += maxx[v].first; return u; } int mid = (L + R) >> 1; ls[u] = merge(ls[u],ls[v],L,mid); rs[u] = merge(rs[u],rs[v],mid,R); pushup(u); return u; } }; struct Trie{ int tot, ch[MAXN][26]; Trie():tot(0){ memset(ch,0,sizeof(ch)); } void insert(const char *s){ int u = 0, n = strlen(s); for(int i = 0; i < n; i++){ int c = s[i] - 'a'; if(!ch[u][c]) ch[u][c] = ++tot; u = ch[u][c]; } } }; struct Suffix_automaton{ Trie trie; SegmentTree seg; int SEGTREE_SIZE; int tot, ch[MAXN][26], pos[MAXN], link[MAXN], par[MAXN][20], len[MAXN]; vector<int> G[MAXN]; Suffix_automaton(){ tot = 0; link[tot] = -1; } int extend(int c, int last){ int p = last, np = ++tot; len[np] = len[last] + 1; while(p!=-1 and !ch[p][c]) ch[p][c] = np, p = link[p]; if(p==-1) link[np] = 0; else{ int q = ch[p][c]; if(len[p]+1==len[q]) link[np] = q; else{ int clone = ++tot; len[clone] = len[p] + 1; link[clone] = link[q]; memcpy(ch[clone],ch[q],sizeof(ch[q])); while(p!=-1 and ch[p][c]==q) ch[p][c] = clone, p = link[p]; link[q] = link[np] = clone; } } return np; } void dfs(int u){ par[u][0] = link[u]; for(int i = 1; ~par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1]; for(int v : G[u]) dfs(v); } void build(){ pos[0] = 0; queue<int> que; que.push(0); while(!que.empty()){ int u = que.front(); que.pop(); for(int c = 0; c < 26; c++){ if(!trie.ch[u][c]) continue; int v = trie.ch[u][c]; pos[v] = extend(c,pos[u]); que.push(v); } } for(int i = 1; i <= tot; i++) G[link[i]].push_back(i); memset(par,255,sizeof(par)); dfs(0); } void modify(string &s, int id){ int u = 0; for(int i = 0; i < (int)s.length(); i++){ int c = s[i] - 'a'; u = ch[u][c]; seg.modify(seg.root[u],id,1,SEGTREE_SIZE+1,1); } } }sam; struct Query{ int l, r, qid; Query(){} Query(int _l, int _r, int id):l(_l),r(_r),qid(id){} }; vector<Query> Q[MAXN]; char s[MAXN], buf[MAXN]; int n, m, sampos[MAXN], matlen[MAXN], l; pair<int,int> ret[MAXN]; vector<string> vec_s; void dfsMerge(int u){ for(int v : sam.G[u]){ dfsMerge(v); sam.seg.root[u] = sam.seg.merge(sam.seg.root[u],sam.seg.root[v],1,sam.SEGTREE_SIZE+1); } for(auto qs : Q[u]){ auto p = sam.seg.ask(qs.l,qs.r+1,1,sam.SEGTREE_SIZE+1,sam.seg.root[u]); if(p.first==0) ret[qs.qid] = make_pair(0,qs.l); else ret[qs.qid] = p; } } void solve(){ scanf("%s %d",s + 1,&n); l = strlen(s + 1); sam.SEGTREE_SIZE = n; vec_s.resize(n); for(int i = 0; i < n; i++){ scanf("%s",buf); vec_s[i] = string(buf); sam.trie.insert(buf); } sam.build(); for(int i = 0; i < n; i++) sam.modify(vec_s[i], i+1); int cur = 0, mat = 0; for(int i = 1; i <= l; i++){ int c = s[i] - 'a'; if(sam.ch[cur][c]) cur = sam.ch[cur][c], mat++; else{ while(cur!=-1 and !sam.ch[cur][c]) cur = sam.link[cur]; if(cur==-1) cur = 0, mat = 0; else mat = sam.len[cur] + 1, cur = sam.ch[cur][c]; } sampos[i] = cur; matlen[i] = mat; } scanf("%d",&m); for(int i = 1; i <= m; i++){ int l, r, pl, pr; scanf("%d %d %d %d",&l,&r,&pl,&pr); int lth = pr - pl + 1; int p = sampos[pr]; if(matlen[pr]<lth) ret[i] = make_pair(0,l); else{ for(int j = 19; ~j; j--) if(sam.par[p][j]!=-1 and sam.len[sam.par[p][j]]>=lth) p = sam.par[p][j]; Q[p].push_back(Query(l,r,i)); } } dfsMerge(0); for(int i = 1; i <= m; i++) cout << ret[i].second << ' ' << ret[i].first << endl; } int main(){ #ifndef ONLINE_JUDGE freopen("Local.in","r",stdin); // freopen("ans.out","w",stdout); #endif solve(); return 0; }