题目大意:
解题思路:
设我们的答案为 m i d mid mid(注意这里有坑是 [ a , b ] [a,b] [a,b]的所有子串和 [ c , d ] [c,d] [c,d]这个子串的最长 l c p lcp lcp),那么我们会发现一个很有趣的事实: 如果 m i d mid mid可行的话,那么任意一个比 m i d mid mid小的数也可行
也就是说,问题满足可二分性,那么我们可以二分答案,将原问题转化为一个判定性问题: m i d mid mid这个答案行不行?
那么我们发现,如果 m i d mid mid这个答案可以的话,就会存在一个后缀 S S S,
1.它的开头在 [ a , b − m i d + 1 ] [a,b-mid+1] [a,b−mid+1]当中。
2. l c p ( S , c ) > = m i d lcp(S,c)>=mid lcp(S,c)>=mid。
那么这里面有两个限制最好处理的是第二个因为我们直知道对于一个后缀 c c c和它 l c p lcp lcp越大的后缀在后缀排序中的 r a n k rank rank是很接近的,那意思就是说如果把这些后缀排好序,那么lcp符合要求的一定是一段连续的区间,(为什么?,因为我们发现排好序以后, l c p lcp lcp这个函数是单峰的,并且峰值在自己这里)
那么我们可以二分出一个 r a n k c ∈ [ r a n k l , r a n k r ] rank_c\in[rank_l,rank_r] rankc∈[rankl,rankr]里面所以的后缀和 c c c的 l c p lcp lcp都是大于等于 m i d mid mid的
然后看看
[
r
a
n
k
l
,
r
a
n
k
r
]
[rank_l,rank_r]
[rankl,rankr]里面有没有
[
a
,
b
−
m
i
d
+
1
]
[a,b-mid+1]
[a,b−mid+1]那就是用
s
a
sa
sa数组了
s
a
[
r
a
n
k
[
i
]
]
=
i
sa[rank[i]] = i
sa[rank[i]]=i
动他区间查询就是主席树啦!!
用rank建主席树
就好了
AC code
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Mid ((l + r + 1) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define hash Hash
#define next Next
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
//.............................
int n, m, len;
int a[maxn];
char s[maxn];
//......................
// sa[l]排序是lth的后缀的开始位置
//ra[l]是起点是l的后缀排名多少
//lcp(suf(i),suf(j)) = min(H(ra[i] + 1),....H(ra[j]));
//区间最小值倍增求
// H(i)是rk[i]和re[i-1]的lcp
//这个板子下标是从0开始,rank从1开始
//传进来的是int数组但是不能有0
struct SA {
int sa[maxn], ra[maxn], height[maxn];
int t1[maxn], t2[maxn], c[maxn];
void build(int *str, int n, int m) {//字符数组,长度,字符种类
str[n] = 0;
n++;
int i, j, p, *x = t1, *y = t2;
for (i = 0; i < m; i++) c[i] = 0;//排序的桶
for (i = 0; i < n; i++) c[x[i] = str[i]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1) {//所有长度为1,2,4,8....的子串的排序
//长度够长的话就是所有的后缀排序
p = 0;
for (i = n - j; i < n; i++) y[p++] = i;
for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = 0; i < m; i++) c[i] = 0;
for (i = 0; i < n; i++) c[x[y[i]]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) ? p - 1 : p++;
if (p >= n) break;
m = p;
}
n--;
for (int i = 0; i <= n; i++) ra[sa[i]] = i;
for (int i = 0, j = 0, k = 0; i <= n; i++) {
if (k) k--;
j = sa[ra[i] - 1];
while (str[i + k] == str[j + k]) k++;
height[ra[i]] = k;
}
st_init(height, n);
}
int lg[maxn], table[23][maxn];
void st_init(int *arr, int n) {
if (!lg[0]) {
lg[0] = -1;
for (int i = 1; i < maxn; i++)
lg[i] = lg[i / 2] + 1;
}
for (int i = 1; i <= n; ++i)
table[0][i] = arr[i];
for (int i = 1; i <= lg[n]; ++i)
for (int j = 1; j <= n; ++j)
if (j + (1 << i) - 1 <= n)
table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
//从j开始长度是2^i次方得st表
}
int lcp(int l, int r) {
l = ra[l], r = ra[r];
if (l > r) swap(l, r);
++l;
int t = lg[r - l + 1];//区间长度
return min(table[t][l], table[t][r - (1 << t) + 1]);
}
} sa;
//.............................
struct node {
int lson, rson;
int val;
} tr[maxn * 40];
int root[maxn], cnt;
void insert(int &now, int pre, int l, int r, int pos) {
now = ++ cnt;
tr[now] = tr[pre];
tr[now].val += 1;
if(l == r) return;
if(pos <= mid) insert(tr[now].lson,tr[pre].lson,l,mid,pos);
else insert(tr[now].rson,tr[pre].rson,mid+1,r,pos);
}
int ask(int ltree, int rtree, int l, int r, int posl, int posr) {
if(posl <= l && posr >= r) return tr[rtree].val - tr[ltree].val;
int ans = 0;
if(posl <= mid) ans += ask(tr[ltree].lson,tr[rtree].lson,l,mid,posl,posr);
if(posr > mid) ans += ask(tr[ltree].rson,tr[rtree].rson,mid+1,r,posl,posr);
return ans;
}
//.............................
bool check(int ans, int a, int b, int c, int d) {
int up, down;
int l = 1, r = sa.ra[c];// 二分下界
while(l < r) {
if(sa.lcp(sa.sa[mid],c) < ans) l = mid + 1;
else r = mid;
}
down = l;
l = sa.ra[c], r = n;
while(l < r) {
if(sa.lcp(sa.sa[Mid],c) < ans) r = Mid - 1;
else l = Mid;
}
up = l;
return (ask(root[down-1],root[up],1,len,a+1,b-ans+2) > 0);
}
int main() {
scanf("%d%d",&n,&m);
scanf("%s",s);
len = strlen(s);
for(int i = 0; i < len; ++ i) a[i] = (int)s[i];
sa.build(a,n,225);
for(int i = 1; i <= len; ++ i) //枚举rank = i
insert(root[i],root[i-1],1,len,sa.sa[i]+1);
while(m --) {
int a, b, c, d;
scanf("%d%d%d%d",&a,&b,&c,&d);
a --, b --, c --, d --;
int l = 0, r = min(b-a+1,d-c+1);
while(l < r) {
if(check(Mid,a,b,c,d)) l = Mid;
else r = Mid - 1;
}
printf("%d\n",l);
}
return 0;
}