[Codeforces 666E] Forensic Examination
[题目链接]
https://codeforces.com/problemset/problem/666/E
[算法]
首先建立广义后缀自动机
对于自动机上的每一个节点建一棵动态开点线段树
对于每次询问 ,
S[pl..pr]所表示节点可以在多串后缀树上倍增得到
那么我们需要的就是计算该节点中出现次数最多的串
线段树合并即可
时间复杂度 : O(NlogN)
[代码]
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int MAXP = 1e5 + 10; const int MAXN = 5e5 + 10; const int ALPHA = 26; const int MAXLOG = 20; int n , m , q; int anc[MAXP][MAXLOG] , rt[MAXP] , pos[MAXN] , len[MAXN]; char S[MAXP] , s[MAXN]; struct Segment_Tree { int sz; int lc[MAXP * 60] , rc[MAXP * 60] , val[MAXP * 60] , loc[MAXP * 60]; Segment_Tree() { sz = 0; } inline void update(int x) { if (val[lc[x]] >= val[rc[x]]) { val[x] = val[lc[x]]; loc[x] = loc[lc[x]]; } else { val[x] = val[rc[x]]; loc[x] = loc[rc[x]]; } } inline void modify(int &now , int l , int r , int x , int value) { if (!now) now = ++sz; if (l == r) { val[now] += value; loc[now] = l; } else { int mid = (l + r) >> 1; if (mid >= x) modify(lc[now] , l , mid , x , value); else modify(rc[now] , mid + 1 , r , x , value); update(now); } } inline int merge(int x , int y , int l , int r) { if (!x || !y) return x + y; int p = ++sz; if (l == r) { val[p] = val[x] + val[y]; loc[p] = l; return p; } int mid = (l + r) >> 1; lc[p] = merge(lc[x] , lc[y] , l , mid); rc[p] = merge(rc[x] , rc[y] , mid + 1 , r); update(p); return p; } inline pair<int , int> query(int now , int l , int r , int ql , int qr) { if (!now) return make_pair(0 , 0); if (l == ql && r == qr) return make_pair(loc[now] , val[now]); int mid = (l + r) >> 1; if (mid >= qr) return query(lc[now] , l , mid , ql , qr); else if (mid + 1 <= ql) return query(rc[now] , mid + 1 , r , ql , qr); else { pair<int , int> t1 = query(lc[now] , l , mid , ql , mid); pair<int , int> t2 = query(rc[now] , mid + 1 , r , mid + 1 , qr); if (t1.second >= t2.second) return t1; else return t2; } } } SGT; struct Suffix_Automaton { int sz , last; int father[MAXP] , child[MAXP][ALPHA] , depth[MAXP]; vector< int > a[MAXP]; multiset< int > s[MAXP]; Suffix_Automaton() { sz = 1; last = 1; } inline int new_node(int dep) { depth[++sz] = dep; memset(child[sz] , 0 , sizeof(child[sz])); father[sz] = 0; a[sz].clear(); s[sz].clear(); return sz; } inline void extend(int ch , int c) { int np = child[last][ch]; if (np) { if (depth[np] == depth[last] + 1) { s[np].insert(c); last = np; } else { int nq = new_node(depth[last] + 1); father[nq] = father[np]; father[np] = nq; memcpy(child[nq] , child[np] , sizeof(child[np])); for (int p = last; child[p][ch] == np; p = father[p]) child[p][ch] = nq; s[nq].insert(c); last = nq; } } else { int np = new_node(depth[last] + 1); int p = last; while (child[p][ch] == 0) { child[p][ch] = np; p = father[p]; } if (child[p][ch] == np) { father[np] = 1; s[np].insert(c); last = np; return; } int q = child[p][ch]; if (depth[q] == depth[p] + 1) { father[np] = q; s[np].insert(c); last = np; return; } else { int nq = new_node(depth[p] + 1); father[nq] = father[q]; father[np] = father[q] = nq; memcpy(child[nq] , child[q] , sizeof(child[q])); while (child[p][ch] == q) { child[p][ch] = nq; p = father[p]; } s[np].insert(c); last = np; return; } } } inline void insert(char *s , int col) { last = 1; for (int i = 1; i <= strlen(s + 1); ++i) extend(s[i] - 'a' , col); } inline void dfs(int u , int par) { anc[u][0] = par; for (int i = 1; i < MAXLOG; ++i) anc[u][i] = anc[anc[u][i - 1]][i - 1]; for (multiset< int > :: iterator it = s[u].begin(); it != s[u].end(); ++it) SGT.modify(rt[u] , 1 , n , *it , 1); for (unsigned i = 0; i < a[u].size(); ++i) { int v = a[u][i]; if (v == par) continue; dfs(v , u); rt[u] = SGT.merge(rt[u] , rt[v] , 1 , n); } } inline void buildtree() { for (int i = 1; i <= sz; ++i) a[father[i]].push_back(i); dfs(1 , 0); } } SAM; template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); } template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { scanf("%s" , s + 1); scanf("%d" , &n); for (int i = 1; i <= n; ++i) { scanf("%s" , S + 1); SAM.insert(S , i); } SAM.buildtree(); m = strlen(s + 1); int now = 1 , L = 0; for (int i = 1; i <= m; ++i) { int ch = s[i] - 'a'; if (SAM.child[now][ch]) { now = SAM.child[now][ch]; ++L; } else { while (now && !SAM.child[now][ch]) now = SAM.father[now]; if (!now) { now = 1; L = 0; } else { L = SAM.depth[now] + 1; now = SAM.child[now][ch]; } } pos[i] = now; len[i] = L; } scanf("%d" , &q); while (q--) { int l , r , pl , pr; scanf("%d%d%d%d" , &l , &r , &pl , &pr); if (len[pr] < pr - pl + 1) { printf("%d %d\n" , l , 0); continue; } int u = pos[pr]; for (int i = MAXLOG - 1; i >= 0; i--) if (SAM.depth[anc[u][i]] >= pr - pl + 1) u = anc[u][i]; pair<int , int> ans = SGT.query(rt[u] , 1 , n , l , r); if (!ans.first) ans.first = l; printf("%d %d\n" , ans.first , ans.second); } return 0; }