題目描述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; }