题目描述
给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ 、$r$ 、$x$ 、$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r$ 中的哪一个里出现次数最多,输出出现次数最多的串编号(如果有多个则输出编号最小的)以及相应出现次数。
$|S|,q\le 5\times 10^5$ ,$\sum\limits_{i=1}^m|T_i|\le 5\times 10^4$ 。
题解
广义后缀自动机+树上倍增+线段树合并
对 $S$ 串和所有 $T_i$ 串的反串放到一起建立广义后缀自动机,得到广义后缀树。
考虑 $S$ 串的 $l...r$ 部分在 $T_i$ 串的出现次数体现为什么:" $S$ 串的 $l...r$ 部分" 在后缀Trie上体现为:顺着 $S$ 的以 $l$ 开头的后缀走到 $S_{l...r}$ 对应节点,该节点是子树内所有后缀的前缀。因此统计的就是该节点子树内有多少个 $T_i$ 的后缀节点。
而现在给出的是后缀树,后缀树相比后缀Trie对无用节点进行压缩,有可能 $S_{l...r}$ 是无用节点。因此要找到的是:最小的 $i\ge r$ ,使得 $S_{l...i}$ 是非无用节点。使用倍增,从底到上求出最靠近根节点的 $dis\ge r-l+1$ 的节点。
问题转化为:求一个点的子树中出现次数最多的颜色是什么。
将询问离线,使用线段树维护子树(right集合)中每种颜色出现的次数,维护区间最大值即最大值位置。DFS整棵树,递归子树后进行线段树合并,最后处理该点对应的询问。
时间复杂度 $O(26n+n\log n)$ 。
#include#include #include #include #include #define N 1100000 #define lson l , mid , ls[x] #define rson mid + 1 , r , rs[x] using namespace std; typedef pair pr; vector vq[N]; int m , pos[N] , c[N][26] , dis[N] , pre[N] , tot = 1 , last = 1 , head[N] , to[N] , next[N] , cnt , fa[N][22] , deep[N] , log[N] , ls[N * 5] , rs[N * 5] , root[N] , tp , ql[N] , qr[N]; pr mx[N * 5] , ans[N]; char str[N]; void extend(int x) { int p = last; if(c[p][x]) { int q = c[p][x]; if(dis[q] == dis[p] + 1) last = q; else { int nq = ++tot; memcpy(c[nq] , c[q] , sizeof(c[q])); dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , last = pre[q] = nq; while(p && c[p][x] == q) c[p][x] = nq , p = pre[p]; } } else { int np = last = ++tot; dis[np] = dis[p] + 1; while(p && !c[p][x]) c[p][x] = np , p = pre[p]; if(!p) pre[np] = 1; else { int q = c[p][x]; if(dis[q] == dis[p] + 1) pre[np] = q; else { int nq = ++tot; memcpy(c[nq] , c[q] , sizeof(c[q])); dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq; while(p && c[p][x] == q) c[p][x] = nq , p = pre[p]; } } } } inline void add(int x , int y) { to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt; } void dfs(int x) { int i; for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]); } int find(int x , int d) { int i; for(i = log[deep[x]] ; ~i ; i -- ) if((1 << i) <= deep[x] && dis[fa[x][i]] >= d) x = fa[x][i]; return x; } inline void pushup(int x) { mx[x] = max(mx[ls[x]] , mx[rs[x]]); } void insert(int p , int l , int r , int &x) { if(!x) x = ++tp; if(l == r) { mx[x].first ++ , mx[x].second = -p; return; } int mid = (l + r) >> 1; if(p <= mid) insert(p , lson); else insert(p , rson); pushup(x); } int merge(int l , int r , int x , int y) { if(!x) return y; if(!y) return x; if(l == r) { mx[x].first += mx[y].first; return x; } int mid = (l + r) >> 1; ls[x] = merge(l , mid , ls[x] , ls[y]); rs[x] = merge(mid + 1 , r , rs[x] , rs[y]); pushup(x); return x; } pr query(int b , int e , int l , int r , int x) { if(b <= l && r <= e) return mx[x]; int mid = (l + r) >> 1; if(e <= mid) return query(b , e , lson); else if(b > mid) return query(b , e , rson); else return max(query(b , e , lson) , query(b , e , rson)); } void solve(int x) { int i; for(i = head[x] ; i ; i = next[i]) solve(to[i]) , root[x] = merge(1 , m , root[x] , root[to[i]]); for(i = 0 ; i < (int)vq[x].size() ; i ++ ) ans[vq[x][i]] = query(ql[vq[x][i]] , qr[vq[x][i]] , 1 , m , root[x]); } inline char nc() { static char buf[100000] , *p1 , *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int readnum() { int ret = 0; char ch = nc(); while(!isdigit(ch)) ch = nc(); while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc(); return ret; } inline int readstr(char *p) { char ch = nc() , *q = p; while(isalpha(ch)) *q ++ = ch , ch = nc(); return q - p; } char pbuf[10000000] , *pp = pbuf; inline void write(int x) { static int sta[20]; int top = 0; if(!x) sta[top ++ ] = 0; while(x) sta[top ++ ] = x % 10 , x /= 10; while(top -- ) *pp ++ = sta[top] ^ '0'; } int main() { int q , i , j , x , y; for(i = readstr(str + 1) ; i ; i -- ) extend(str[i] - 'a') , pos[i] = last; m = readnum(); for(i = 1 ; i <= m ; i ++ ) { last = 1; for(j = readstr(str + 1) ; j ; j -- ) extend(str[j] - 'a') , insert(i , 1 , m , root[last]); } for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i) , log[i] = log[i >> 1] + 1; dfs(1); q = readnum(); for(i = 1 ; i <= q ; i ++ ) { ql[i] = readnum() , qr[i] = readnum() , x = readnum() , y = readnum(); vq[find(pos[x] , y - x + 1)].push_back(i); } solve(1); for(i = 1 ; i <= q ; i ++ ) write(ans[i].first ? -ans[i].second : ql[i]) , *pp ++ = ' ' , write(ans[i].first) , *pp ++ = '\n'; fwrite(pbuf , 1 , pp - pbuf , stdout); return 0; }