题目大意:给一个字符串多次询问一个子串s的权值。一个字符串的权值定义为最长的出现了至少两次的子串的长度。 n , m ≤ 1 0 5 n,m\le10^5 n,m≤105。
题解:考虑把串反过来,建SAM。显然询问要先二分一波。
那就是求对应下标在某个区间内的点是否存在两个点其LCA的val大于等于二分的值。
考虑哪些点对有可能成为答案,显然如果有三个前缀下标a,b,c满足a L C A ( p s ( a ) , p s ( b ) ) = L C A ( p s ( a ) , p s ( c ) ) LCA(ps(a),ps(b))=LCA(ps(a),ps(c)) LCA(ps(a),ps(b))=LCA(ps(a),ps(c)),那么显然选择(a,c)一定不会比(a,b)优,因此启发式合并的求出所有有可能成为答案的点对,有 O ( n l g n ) O(nlgn) O(nlgn)个,所有可能成为答案的点形如(a,b,val)(假设a>b)。然后就是一个矩形询问最大值的问题,发现可以离线后直接线段树即可。
#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<
#define sp <<" "
#define ln <
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;inline int gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
inline int inn() { int x,ch;while((ch=gc())<'0'||ch>'9');x=ch^'0';while((ch=gc())>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^'0');return x; }
inline int instr(char *s) { int n=0,ch;while((ch=gc())<'a'||ch>'z');s[++n]=char(ch);while((ch=gc())>='a'&&ch<='z') s[++n]=char(ch);return n; }
}using INPUT_SPACE::inn;using INPUT_SPACE::instr;
namespace OUTPUT_SPACE{
char ss[500000*15],tt[20];int ssl,ttl;inline int Flush() { return fwrite(ss+1,sizeof(char),ssl,stdout),ssl=0,0; }
inline int print(int x) { if(!x) ss[++ssl]='0';for(ttl=0;x;x/=10) tt[++ttl]=char(x%10+'0');for(;ttl;ttl--) ss[++ssl]=tt[ttl];return ss[++ssl]='\n'; }
}using OUTPUT_SPACE::print;using OUTPUT_SPACE::Flush;
const int N=200010,SIG=26,INF=INT_MAX/10;
vector<pii> ps[N],q[N];char s[N];int ans[N];
struct SAM{
int val[N],fa[N],ch[N][SIG],rt,las,node_cnt,id[N];
set<int> s[N];vector<int> g[N];
inline int new_node(int v) { return val[++node_cnt]=v,node_cnt; }
inline int _init() { return node_cnt=0,rt=las=new_node(0); }
inline int extend(int w,int i)
{
int p=las,np=new_node(val[p]+1);
while(p&&!ch[p][w]) ch[p][w]=np,p=fa[p];
if(!p) fa[np]=rt;
else{
int q=ch[p][w],v=val[p]+1;
if(val[q]==v) fa[np]=q;
else{
int nq=new_node(v);
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
while(p&&ch[p][w]==q) ch[p][w]=nq,p=fa[p];
}
}
return id[las=np]=i;
}
inline int dfs(int x)
{
if(id[x]) s[x].insert(id[x]);s[x].insert(INF);
Rep(i,g[x])
{
int y=g[x][i];dfs(y);
if(s[x].size()<s[y].size()) s[x].swap(s[y]);
if(s[y].empty()) continue;
for(sit it=s[y].begin();it!=s[y].end();it++) if(*it!=INF)
{
sit it2=s[x].lower_bound(*it);
if(*it2!=INF) ps[*it2].pb(mp(*it,val[x]));
if(it2!=s[x].begin()) it2--,ps[*it].pb(mp(*it2,val[x]));
}
for(sit it=s[y].begin();it!=s[y].end();it++) s[x].insert(*it);set<int>().swap(s[y]);
}
return 0;
}
inline int solve() { rep(i,2,node_cnt) g[fa[i]].pb(i);return dfs(rt); }
}t;
struct segment{
int v,l,r;segment *ch[2];
}*rt;
inline int build(segment* &rt,int l,int r)
{
rt=new segment,rt->l=l,rt->r=r,rt->v=0;
if(l==r) return 0;int mid=(l+r)>>1;
return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r);
}
inline int update(segment* &rt,int p,int v)
{
int l=rt->l,r=rt->r,mid=(l+r)>>1;rt->v=max(rt->v,v);
if(l==r) return 0;return update(rt->ch[p>mid],p,v);
}
inline int query(segment* &rt,int s,int t)
{
int l=rt->l,r=rt->r,mid=(l+r)>>1;
if(s<=l&&r<=t) return rt->v;int ans=0;
if(s<=mid) ans=max(ans,query(rt->ch[0],s,t));
if(mid<t) ans=max(ans,query(rt->ch[1],s,t));
return ans;
}
inline int check(int l,int r,int x) { return query(rt,l+x-1,r)>=x; }
inline int query(int l,int r)
{
int L=1,R=r-l+1;
while(L<=R)
{
int mid=(L+R)>>1;
if(check(l,r,mid)) L=mid+1;
else R=mid-1;
}
return R;
}
int main()
{
int n=inn(),m=inn(),r;instr(s),t._init();
reverse(s+1,s+n+1);rep(i,1,n) t.extend(s[i]-'a',i);
t.solve(),build(rt,1,n);
rep(i,1,m) r=n-inn()+1,q[r].pb(mp(n-inn()+1,i));
rep(i,1,n)
{
Rep(j,ps[i]) update(rt,ps[i][j].fir,ps[i][j].sec);
Rep(j,q[i]) ans[q[i][j].sec]=query(q[i][j].fir,i);
}
rep(i,1,m) print(ans[i]);return Flush(),0;
}