题目大意
给出一个长度为
题解
因为要在原串的前缀上分析后缀十分的别扭,所以对这个问题略作转化:将原串翻转过来,求左端点在区间
问题已经转化为了:左端点在区间
只分析到这里的话,对于每个询问可以
但是这样的复杂度是非常不可观的,分析后缀间lcp的性质:若有三个左端点分别为
有了上面的性质,虽然无法直接优化上面的做法,但是将所有的询问离线后这个性质就显得很有用了。考虑将所有询问离线后莫队,对于每个添加操作,就相当于在原有的一堆散点中插入了一个散点,新插入的点两侧的点原有的答案就一定不比新点与两侧的答案优,这些过程用几个多重集进行即可。对于每个删除操作就相当于添加操作的逆操作。这样做的时间复杂度为
考虑如何去掉这个log。发现添加操作是无法去掉log的,那么我们就干脆去掉添加操作,用只带删除的回滚莫队来解决这个问题。考虑用删除操作时用双向链表维护,每删除一个位置就修改其前驱和后继元素的指针并更新答案。这样就可以去掉维护位置的log了,因为虽然修改的次数很多,但是对修改后的答案进行的查询只有q个,所以考虑用分块维护链表中相邻位置的两后缀的lcp的长度,这样就可以每次
回滚莫队见这里。
代码
#include <cstdio>
#include <iostream>
//#include <ctime>
#include <algorithm>
using namespace std;
inline int read() {
register int val=0; char ch;
while(~(ch=getchar()) && (ch<'0' || ch>'9')); val=ch-'0';
while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';
return val;
}
const int maxn=int(1e5)+111;
int n,m;
char s[maxn];
int siz, num, bel[maxn];
int ans[maxn];
struct Query {
int l,r,id;
Query() {}
Query(int a,int b,int c):l(a),r(b),id(c) {}
bool operator < (const Query &b) const {
return bel[l]==bel[b.l]?r>b.r:bel[l]<bel[b.l];
}
}q[maxn];
void Read() {
register int i;
n=read(), m=read();
scanf("%s",s);
reverse(s,s+n);
while(siz*siz<n) ++siz;
for(i=1;i<=n;++i)
bel[i]=(i-1)/siz+1;
num=bel[n];
int l,r;
for(i=1;i<=m;++i) {
l=read(), r=read();
q[i]=Query(n-r+1,n-l+1,i);
}
sort(q+1,q+1+m);
return;
}
int K;
int sa[maxn], rnk[maxn], tmp[maxn];
int h[maxn];
int st[maxn][20];
inline bool cmp(const int &a,const int &b) {
if(rnk[a]!=rnk[b]) return rnk[a]<rnk[b];
else {
int x=a+K<n?rnk[a+K]:-1;
int y=b+K<n?rnk[b+K]:-1;
return x<y;
}
}
void Init_sa() {
register int i;
for(i=0;i<=n;++i) {
sa[i]=i;
rnk[i]=i<n?s[i]:-1;
}
for(K=1;K<=n;K<<=1) {
sort(sa,sa+1+n,cmp);
tmp[sa[0]]=1;
for(i=1;i<=n;++i)
if(cmp(sa[i-1],sa[i])) tmp[sa[i]]=tmp[sa[i-1]]+1;
else tmp[sa[i]]=tmp[sa[i-1]];
for(i=0;i<=n;++i)
rnk[i]=tmp[i];
}
return;
}
void Init_lcp(int *lcp) {
register int i, j, h=0;
for(i=0;i<=n;++i) {
rnk[sa[i]]=i;
lcp[i]=0;
}
lcp[sa[0]]=0;
for(i=0;i<n;++i) {
j=sa[rnk[i]-1];
if(h>0) --h;
while(i+h<n && j+h<n && s[i+h]==s[j+h]) ++h;
lcp[rnk[j]]=h;
}
return;
}
#define bin(k) (1<<(k))
int lg2[maxn];
void Init_lg2() {
register int i,j;
for(i=1,j=0;i<maxn;i<<=1,++j) lg2[i]=j;
for(i=3;i<maxn;++i) if(!lg2[i]) lg2[i]=lg2[i-1];
return;
}
void Init_st() {
register int i,j;
for(i=1;i<=n;++i)
st[i][0]=h[i];
int upp=lg2[n];
for(i=1;i<=upp;++i)
for(j=1;j+bin(i)-1<=n;++j)
st[j][i]=min(st[j][i-1],st[j+bin(i-1)][i-1]);
return;
}
inline int RMQ(const int &l,const int &r) {
int lg=lg2[r-l+1];
return min(st[l][lg],st[r-bin(lg)+1][lg]);
}
void Init() {
Init_lg2();
Init_sa();
Init_lcp(h);
Init_st();
return;
}
bool used[maxn];
int pre[maxn], nex[maxn];
int cnt[maxn], bcnt[400];
int getmax() {
register int i,j;
for(i=num;!bcnt[i];--i);
for(j=i*siz;!cnt[j];--j);
return j;
}
void pre_calc(int id) {
register int i;
for(i=0;i<=n;++i) {
used[i]=false;
cnt[i]=bcnt[bel[i]]=pre[i]=nex[i]=0;
}
for(i=(id-1)*siz+1;i<=n;++i)
used[rnk[i-1]]=true;
int cur, last=0;
for(i=1;i<=n;++i) if(used[i]) {
pre[i]=last;
nex[last]=i;
if(last) ++cnt[cur=RMQ(last,i-1)], ++bcnt[bel[cur]];
last=i;
}
nex[last]=n+1;
return;
}
#define mp make_pair
pair<int,int> s1[maxn], s2[maxn], s3[maxn]; //pre, nex, cnt
int t1,t2,t3;
void Del(int id,int sign) {
id=rnk[id-1];
int cur, tot=0;
if(pre[id]>=1) {
cur=RMQ(pre[id],id-1); ++tot;
if(sign) s3[++t3]=mp(cur,-1);
--cnt[cur], --bcnt[bel[cur]];
if(sign) s2[++t2]=mp(pre[id],nex[pre[id]]);
nex[pre[id]]=nex[id];
}
if(nex[id]<=n) {
cur=RMQ(id,nex[id]-1); ++tot;
if(sign) s3[++t3]=mp(cur,-1);
--cnt[cur], --bcnt[bel[cur]];
if(sign) s1[++t1]=mp(nex[id],pre[nex[id]]);
pre[nex[id]]=pre[id];
}
if(tot==2) {
cur=RMQ(pre[id],nex[id]-1);
if(sign) s3[++t3]=mp(cur,1);
++cnt[cur], ++bcnt[bel[cur]];
}
return;
}
void Rollback() {
while(t1) {
pre[s1[t1].first]=s1[t1].second;
--t1;
}
while(t2) {
nex[s2[t2].first]=s2[t2].second;
--t2;
}
while(t3) {
cnt[s3[t3].first]-=s3[t3].second;
bcnt[bel[s3[t3].first]]-=s3[t3].second;
--t3;
}
return;
}
int Mo(int pos,int id) {
register int i=pos, ql=(id-1)*siz+1, qr=n;
pre_calc(id);
for(i=pos;bel[q[i].l]==id;++i) {
while(qr>q[i].r) Del(qr--,0);
while(ql<q[i].l) Del(ql++,1);
ans[q[i].id]=getmax();
Rollback(); ql=(id-1)*siz+1;
}
return i;
}
void Solve() {
register int i,pos=1;
for(i=1;i<=num;++i)
pos=Mo(pos,i);
return;
}
int main() {
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
// double t1=clock();
Read();
Init();
Solve();
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
// printf("%.3lfsec\n",(clock()-t1)/CLOCKS_PER_SEC);
return 0;
}