题目描述
给出只包含小写字母a,b的两个字符串s,t,q次询问,每次询问s[l..r]和t的最长公共子串长度。
基数排序
复习SA的时候发现了不得了的东西
并不需要真正一维维排序
在每一层的时候按合并的后半段把sa数组搞出来,rank重了就随便放
这样就把第二维排序了,按第二维的顺序加,新的rank=第一维小于它的+第一维等于它的之前加的个数
实测建SA跑0.5s,虽然还是很慢但比基数排序+vector的2s高到不知道哪里去了
题解
把s和t都建出SA,求s中每个后缀的最长是t子串的前缀
当后缀确定了之后,排完序的t中的lcp是上凸的,所以可以三分单调计算
单调的时候要注意lcp相同的情况,通过判断第一位不同的与询问串的那一位的大小关系可以知道后面是否有更长的lcp,如果大于就不可能有更长的,小于的话往后跳也不会影响到后面的询问后缀(字典序递增)
因为询问有r边界所以离线+线段树*2即可
code
比第二高(man)到不知道哪里去了
#include
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (ab?a:b)
#define mod 998244353
#define ll long long
//#define file
using namespace std;
struct qs{int x,y,id;} q[200001];
int tr[2][800001],b[200001],ans[200001],Q,i,j,k,l,x,y,s1,s2;
ll p[200001];
bool cmp(qs a,qs b) {return a.x>b.x;}
void change(int T,int t,int l,int r,int x,int s)
{
int mid=(l+r)/2;
tr[T][t]=(!T)?max(tr[T][t],s):min(tr[T][t],s);
if (l==r) return;
if (x<=mid) change(T,t*2,l,mid,x,s);
else change(T,t*2+1,mid+1,r,x,s);
}
int find(int T,int t,int l,int r,int x,int y)
{
int mid=(l+r)/2;
int ans=(!T)?0:2133333333,s;
if (x<=l && r<=y) return tr[T][t];
if (x<=mid) s=find(T,t*2,l,mid,x,y),ans=(!T)?max(ans,s):min(ans,s);
if (mids2 || s1==s2 && (st1.sa[i]+s2-1==st1.n || st2.sa[j+1]+s2-1st1.s[st1.sa[i]+s2])) break;
++j;
}
b[st1.sa[i]]=js(st1.sa[i],st2.sa[j]);
}
}
int main()
{
// freopen("loj3298.in","r",stdin);
// #ifdef file
// freopen("loj3298.out","w",stdout);
// #endif
memset(tr[1],127,sizeof(tr[1]));
p[0]=1;
fo(i,1,200000) p[i]=p[i-1]*2%mod;
scanf("%s",st1.s+1),st1.n=strlen(st1.s+1);st1.build();
scanf("%s",st2.s+1),st2.n=strlen(st2.s+1);st2.build();
init();
scanf("%d",&Q);
fo(i,1,Q) scanf("%d%d",&q[i].x,&q[i].y),q[i].id=i;
sort(q+1,q+Q+1,cmp);
j=1;
fd(i,st1.n,1)
{
change(0,1,1,st1.n,i+b[i]-1,b[i]);
change(1,1,1,st1.n,i+b[i]-1,i);
while (j<=Q && q[j].x==i)
{
ans[q[j].id]=find(0,1,1,st1.n,q[j].x,q[j].y);
if (q[j].y