题目描述node
佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了一个长为 \(n\) 的字符串 \(s\),和 \(m\) 个问题。佳媛姐姐必须正确回答这 \(m\)个问题,才能打开箱子拿到礼物,升职加薪,出任 \(CEO\),嫁给高富帅,走上人生巅峰。每一个问题均有 \(a,b,c,d\) 四个参数,问你子串 \(s[a…b]\) 的全部子串和 \(s[c…d]\) 的最长公共前缀的长度的最大值是多少?佳媛姐姐并不擅长作这样的问题,因此她向你求助,你该如何帮助她呢?c++
\(1 \leq n, m \leq 100000, \ a \leq b, \ c \leq d, \ 1 \leq a, b, c, d \leq n\)git
写\(sam\)是确定会去写的,这样才学的了字符串,后缀数组又不会用,\(sam\)套上数据结构的感受就像回家同样
里面又能剖分又能线段树合并,调试又好调,我爱死这种写法了 \(qwq\)数组
问题求一个字符串的前缀最多能和另外一个字符串的全部子串匹配多少, 不妨二分答案判断这个前缀是否在这些子串里出现过数据结构
考虑对母串建 \(sam\) ,求出原串中每个后缀在 \(sam\) 上的对应节点,那么对于须要\(check\) 的前缀 \([c, c + len -1]\) ,能够快速倍增找到其在前缀树上对应的节点spa
设找到的节点为 \(u\) ,问题就转化为 \(u\) 的 \(right\) 集合中,是否存在一个来自于 \([a+len-1, b]\) 的后缀调试
因此,直接大力线段树合并维护 \(parent\) 树上每一个节点的 \(right\) 集合便可,查询只须要判断对应线段树的 \([a+len-1, b]\) 的和是否 \(>=1\),复杂度是 \(O(mlog^2n)\)code
/*program by mangoyang*/ #include<bits/stdc++.h> #define inf (0x7f7f7f7f) #define Max(a, b) ((a) > (b) ? (a) : (b)) #define Min(a, b) ((a) < (b) ? (a) : (b)) #define N (200005) typedef long long ll; using namespace std; template <class T> inline void read(T &x){ int f = 0, ch = 0; x = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0'; if(f) x = -x; } char s[N]; int n, m; struct Segment_Tree{ int sum[N*25], lc[N*25], rc[N*25], cnt; inline void modify(int &u, int l, int r, int pos){ u = ++cnt; if(l == r) return (void) (sum[u]++); int mid = l + r >> 1; if(pos <= mid) modify(lc[u], l, mid, pos); else modify(rc[u], mid + 1, r, pos); sum[u] = sum[lc[u]] + sum[rc[u]]; } inline int merge(int x, int y, int l, int r){ if(!x || !y) return x + y; int o = ++cnt; if(l == r) sum[o] = sum[x] + sum[y]; else{ int mid = l + r >> 1; lc[o] = merge(lc[x], lc[y], l, mid); rc[o] = merge(rc[x], rc[y], mid + 1, r); sum[o] = sum[lc[o]] + sum[rc[o]]; } return o; } inline int query(int u, int l, int r, int L, int R){ if(!u) return 0; if(l >= L && r <= R) return sum[u]; int mid = l + r >> 1, res = 0; if(L <= mid) res += query(lc[u], l, mid, L, R); if(mid < R) res += query(rc[u], mid + 1, r, L, R); return res; } }Seg; struct Suffix_Automaton{ int f[N][23], rt[N<<1], buf[N], a[N]; int ch[N][26], fa[N], dep[N], pos[N], tail, size; inline Suffix_Automaton(){ tail = size = 1; } inline int newnode(int x){ dep[++size] = x; return size; } inline void ins(int c, int id){ int p = tail, np = newnode(dep[p] + 1); Seg.modify(rt[np], 1, n, id), pos[id] = np; for(; p && !ch[p][c]; p = fa[p]) ch[p][c] = np; if(!p) return (void) (fa[np] = 1, tail = np); int q = ch[p][c]; if(dep[q] == dep[p] + 1) fa[np] = q; else{ int nq = newnode(dep[p] + 1); fa[nq] = fa[q], fa[q] = fa[np] = nq; for(int i = 0; i < 26; i++) ch[nq][i] = ch[q][i]; for(; p && ch[p][c] == q; p = fa[p]) ch[p][c] = nq; }tail = np; } inline void prepare(){ for(int i = 1; i <= size; i++) f[i][0] = fa[i]; for(int j = 1; j <= 22; j++) for(int i = 1; i <= size; i++) f[i][j] = f[f[i][j-1]][j-1]; for(int i = 1; i <= size; i++) buf[dep[i]]++; for(int i = 1; i <= size; i++) buf[i] += buf[i-1]; for(int i = 1; i <= size; i++) a[buf[dep[i]]--] = i; for(int i = size; i >= 2; i--){ int u = a[i]; rt[fa[u]] = Seg.merge(rt[u], rt[fa[u]], 1, n); } } inline bool check(int x, int len, int l, int r){ x = pos[x]; for(int i = 22; i >= 0; i--) if(dep[f[x][i]] >= len) x = f[x][i]; return Seg.query(rt[x], 1, n, l, r) >= 1; } }van; inline int solve(int a, int b, int c, int d){ int l = 1, r = min(b - a + 1, d - c + 1), ans = 0; while(l <= r){ int mid = l + r >> 1; if(van.check(c + mid - 1, mid, a + mid - 1, b)) ans = mid, l = mid + 1; else r = mid - 1; } return ans; } int main(){ read(n), read(m), scanf("%s", s + 1); for(int i = 1; i <= n; i++) van.ins(s[i] - 'a', i); van.prepare(); for(int i = 1; i <= m; i++){ int a, b, c, d; read(a), read(b), read(c), read(d); printf("%d\n", solve(a, b, c, d)); } return 0; }