后缀数组+可持久化线段树+二分
啊啊啊智商好低,想了好久。
一个直观的想法是在s[a…b]中找到和s[c…d]最接近的串,使得height最大。然而一个很烦的事情是s[a…b]存在一个右边界b,意味着我们需要min一个边界值。遇到这种min啊max啊的东西一般考虑强行分类讨论。
于是二分一个答案mid,那能贡献mid答案的串开头一定位于s[a…b-mid+1]之中,那边界的条件就没了,这样就可以直接用height了。这东西可持久化线段树套一下即可。
#include
#include
#include
#define N 200005
#define cmin(u,v) (u)>(v)?(u)=(v):0
#define cmax(u,v) (u)<(v)?(u)=(v):0
using namespace std;
namespace runzhe2000
{
const int INF = 1<<30;
char s[N];
int n, m, t1[N], t2[N], sa[N], rank[N], sum[N], height[N], lef, rig;
void SA_build()
{
int *x = t1, *y = t2, m = 30;
for(int i = 1; i <= n; i++) sum[x[i] = s[i] - 'a' + 1]++;
for(int i = 1; i <= m; i++) sum[i] += sum[i-1];
for(int i = n; i >= 1; i--) sa[sum[x[i]]--] = i;
for(int k = 1; k <= n; k <<= 1)
{
int p = 0;
for(int i = n-k+1; i <= n; i++) y[++p] = i;
for(int i = 1; i <= n; i++) if(sa[i] - k > 0) y[++p] = sa[i] - k;
for(int i = 1; i <= m; i++) sum[i] = 0;
for(int i = 1; i <= n; i++) sum[x[i]]++;
for(int i = 1; i <= m; i++) sum[i] += sum[i-1];
for(int i = n; i >= 1; i--) sa[sum[x[y[i]]]--] = y[i];
swap(x, y);
for(int i = 1; i <= n; i++)
x[sa[i]] = x[sa[i-1]] + (y[sa[i]] == y[sa[i-1]] && y[sa[i]+k] == y[sa[i-1]+k] ? 0 : 1);
m = x[sa[n]];
if(m == n) break;
}
for(int i = 1; i <= n; i++) rank[sa[i]] = i;
for(int i = 1, k = 0; i <= n; height[rank[i++]] = k?k--:k)
for(; s[i+k] == s[sa[rank[i]-1]+k]; k++);
}
struct seg
{
int mi, cnt;
seg *ch[2];
void pushup()
{
mi = min(ch[0]->mi, ch[1]->mi);
cnt = ch[0]->cnt + ch[1]->cnt;
}
}mem[N*15], *tot, *null, *root[N];
seg* seg_new()
{
seg *p = ++tot;
*p = *null;
return p;
}
void seg_init()
{
null = tot = mem;
null->mi = INF, null->cnt = 0;
null->ch[0] = null->ch[1] = null;
for(int i = 0; i <= n; i++) root[i] = seg_new();
}
void seg_make(seg *x, int l, int r)
{
if(l == r){x -> mi = height[l]; return;}
int mid = (l+r)>>1;
x->ch[0] = seg_new();
x->ch[1] = seg_new();
seg_make(x->ch[0], l, mid);
seg_make(x->ch[1], mid+1, r);
x->pushup();
}
void seg_ins(seg *x, seg *y, int l, int r, int pos)
{
*y = *x;
if(l == r){y->cnt++; return;};
int mid = (l+r)>>1;
if(pos <= mid)
{
y->ch[0] = seg_new(); y->ch[1] = x->ch[1];
seg_ins(x->ch[0], y->ch[0], l, mid, pos);
}
else
{
y->ch[0] = x->ch[0]; y->ch[1] = seg_new();
seg_ins(x->ch[1], y->ch[1], mid+1, r, pos);
}
y->pushup();
}
bool find(seg *x, seg *y, int l, int r, int ql, int qr, int isright)
{
if(ql > qr) return 0;
if(ql <= l && r <= qr)
{
if(y->cnt - x->cnt == 0) return 0;
else if(l == r)
{
if(!isright) cmax(lef, l);
else cmin(rig, l);
return 1;
}
}
int mid = (l+r)>>1;
if(!isright) // left max
{
if(mid < qr) {if(find(x->ch[1], y->ch[1], mid+1, r, ql, qr, isright)) return 1;}
if(ql <= mid) return find(x->ch[0], y->ch[0], l, mid, ql, qr, isright);
}
else // right min
{
if(ql <= mid) {if(find(x->ch[0], y->ch[0], l, mid, ql, qr, isright)) return 1;}
if(mid < qr) return find(x->ch[1], y->ch[1], mid+1, r, ql, qr, isright);
}
}
int query(seg *x, int l, int r, int ql, int qr)
{
if(ql > qr) return INF;
if(ql <= l && r <= qr)return x->mi;
int mid = (l+r)>>1, p1 = INF, p2 = INF;
if(ql <= mid) p1 = query(x->ch[0], l, mid, ql, qr);
if(mid < qr) p2 = query(x->ch[1], mid+1, r, ql, qr);
return min(p1, p2);
}
void main()
{
int happy = scanf("%d%d%s",&n,&m,s+1);
s[++n] = 'z' + 1;
SA_build();
seg_init();
seg_make(root[0],1,n);
for(int i = 1; i <= n; i++)
seg_ins(root[i-1], root[i], 1, n, rank[i]);
for(int i = 1; i <= m; i++)
{
int a, b, c, d, p, l, r;
happy = scanf("%d%d%d%d",&a,&b,&c,&d);
l = 0, r = min(d-c+1, b-a+1);
for(; l < r; )
{
int mid = (l+r+1)>>1, ans; lef = 0; rig = n;
find(root[a-1], root[b-mid+1], 1, n, 1, rank[c], 0);
find(root[a-1], root[b-mid+1], 1, n, rank[c], n, 1);
ans = max(query(root[0], 1, n, lef+1, rank[c]), query(root[0], 1, n, rank[c]+1, rig));
cmin(ans, mid);
if(ans < mid) r = mid - 1;
else l = mid;
}
printf("%d\n",l);
}
}
}
int main()
{
runzhe2000::main();
}