• loj#3298. 「BJOI2020」封印(后缀数组SA)


    题目描述

    给出只包含小写字母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 <bits/stdc++.h>
    #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) (a<b?a:b)
    #define max(a,b) (a>b?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 (mid<y) s=find(T,t*2+1,mid+1,r,x,y),ans=(!T)?max(ans,s):min(ans,s);
    	return ans;
    }
    
    struct sa{
    	int n,rk[200001],Rk[200001],sa[200001],h[200001],a[200002],b[200002];
    	char s[200001];
    	ll hs[200001];
    	
    	void put()
    	{
    		memset(a,0,sizeof(a));memset(b,0,sizeof(b));
    		fo(i,1,n) Rk[i]=((i+l<=n)?rk[i+l]:0)+1;
    		fo(i,1,n) ++a[Rk[i]];
    		fo(i,1,n+1) a[i]+=a[i-1];
    		fo(i,1,n) ++b[Rk[i]],sa[a[Rk[i]-1]+b[Rk[i]]]=i;
    	}
    	ll get(int t) {return 1ll*a[t]*(n+1)+((t+l<=n)?a[t+l]:0);}
    	ll Get(int x,int y) {return ((hs[y]-hs[x-1]*p[y-x+1])%mod+mod)%mod;}
    	void build()
    	{
    		fo(i,1,n) hs[i]=(hs[i-1]*2+(s[i]-'a'))%mod;
    		fo(i,1,n) rk[i]=s[i]-'a'+1;
    		l=1;
    		while (l<n)
    		{
    			put();
    			memset(a,0,sizeof(a));memset(b,0,sizeof(b));
    			fo(i,1,n) ++a[rk[i]];
    			fo(i,1,n) a[i]+=a[i-1];
    			fo(i,1,n) ++b[rk[sa[i]]],Rk[sa[i]]=a[rk[sa[i]]-1]+b[rk[sa[i]]];
    			
    			memcpy(a,rk,(n+1)*4);
    			memcpy(rk,Rk,(n+1)*4);
    			fo(i,1,n) sa[rk[i]]=i;
    			j=0;
    			fo(i,1,n)
    			j+=(i==1) || get(sa[i-1])!=get(sa[i]),rk[sa[i]]=j;
    			l*=2;
    		}
    		fo(i,1,n) sa[rk[i]]=i;
    	}
    } st1,st2;
    int js(int t1,int t2)
    {
    	int l=1,r=min(st1.n-t1+1,st2.n-t2+1),mid;
    	while (l<r)
    	{
    		mid=(l+r)/2;
    		if (st1.Get(t1,t1+mid-1)==st2.Get(t2,t2+mid-1)) l=mid+1; else r=mid;
    	}
    	l-=st1.s[t1+l-1]!=st2.s[t2+l-1];
    	return l;
    }
    void init()
    {
    	j=1;
    	fo(i,1,st1.n)
    	{
    		while (j<st2.n)
    		{
    			s1=js(st1.sa[i],st2.sa[j]),s2=js(st1.sa[i],st2.sa[j+1]);
    			if (s1>s2 || s1==s2 && (st1.sa[i]+s2-1==st1.n || st2.sa[j+1]+s2-1<st2.n && st2.s[st2.sa[j+1]+s2]>st1.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<st1.n) k=find(1,1,1,st1.n,q[j].y+1,st1.n),ans[q[j].id]=max(ans[q[j].id],q[j].y-k+1);
    			++j;
    		}
    	}
    	
    	fo(i,1,Q) printf("%d
    ",ans[i]);
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    树的最小支配集 最小点覆盖 与 最大独立集 (图论)
    P1993 小K的农场 (差分约束)
    P1168 中位数 (优先队列,巧解)
    STL 优先队列
    P3799 妖梦拼木棒 (组合数学)
    P2389 电脑班的裁员 (动态规划)
    3-Java中基本数据类型的存储方式和相关内存的处理方式(java程序员必读经典)
    1-匿名对象
    2-封装性
    2-递归调用
  • 原文地址:https://www.cnblogs.com/gmh77/p/13161190.html
Copyright © 2020-2023  润新知