zoukankan      html  css  js  c++  java
  • 题解 LOJ3298 「BJOI2020」封印(SAM,数据结构)

    题目大意

    题目链接

    给出只包含小写字母( ext{a}, ext{b})的两个字符串(s,t)(q)次询问,每次询问(s[ldots r])(t)的最长公共子串长度。

    数据范围:(1leq|s|,|t|,qleq 2 imes10^5)

    本题题解

    考虑预处理(s)的每个子串是不是(t)的子串。发现对于一个左端点(i) ((1leq ileq |s|)),一定存在一个(R[i]),使得(forall jin[i,R[i]]:s[idots j])都是(t)的子串,(forall kin[R[i]+1,n]:s[idots k])都不是(t)的子串。也就是说,使得(s[idots r])(t)子串的(r),一定是从(i)开始的一段连续的区间,而(R[i])就是其中最大的(r)。我们考虑把所有(R[i])预处理出来。

    从小到大枚举(i)。我们已经知道了(s[i-1dots R[i-1]])(t)的子串。那么,(s[idots R[i-1]])一定也是(t)的子串,也就是说,(R[i]geq R[i-1])。那么我们从(R[i-1]+1)开始,一位一位向后枚举,判断是否是(t)的子串。可以对(t)建一个SAM,这个“向后枚举”,就相当于在SAM上走转移边。同时,我们还要支持把前面的第(i-1)位删掉,这就相当于在SAM上跳父亲边。因为(R[i])是单调的,所以时间复杂度(O(|s|))(|s|,|t|)同阶)。

    预处理出(R)数组后,考虑回答询问。对于一个询问(l,r)。我们相当于要求出,(max_{i=l}^{r}{min(r,R[i])-i+1})。对于(min(r,R[i])),我们分类讨论:

    • 对于(R[i]leq r)(i),相当于询问(max_{lleq ileq r}{R[i]-i+1})
    • 对于(R[i]>r)(i),相当于询问(r+max_{lleq ileq r}{-i}+1)

    如果把(R[i])(r)的关系看做一维,(i)(l,r)的关系看做一维,那相当于是一个二维的区间最大值查询。可以考虑离线,把询问按右端点排序,这样(R[i])(r)的这一维就不存在了,我们只要做一维的区间最大值查询,可以用(两棵)线段树维护。

    时间复杂度(O(|s|+qlog |s|))

    参考代码(在LOJ查看):

    //problem:LOJ3298
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    const int MAXN=2e5;
    const int INF=1e9;
    
    int n,m,q,R[MAXN+5];
    char s[MAXN+5],t[MAXN+5];
    struct SAM{
    	int cnt,ed,mp[MAXN*2+5][2],fa[MAXN*2+5],len[MAXN*2+5];
    	void ins(int c){
    		int p=ed;ed=++cnt;len[ed]=len[p]+1;
    		for(;p && !mp[p][c];p=fa[p])mp[p][c]=ed;
    		if(!p)fa[ed]=1;
    		else{
    			int q=mp[p][c];
    			if(len[q]==len[p]+1)fa[ed]=q;
    			else{
    				len[++cnt]=len[p]+1;
    				for(int i=0;i<2;++i)mp[cnt][i]=mp[q][i];
    				fa[cnt]=fa[q];fa[q]=fa[ed]=cnt;
    				for(;mp[p][c]==q;p=fa[p])mp[p][c]=cnt;
    			}
    		}
    	}
    	SAM(){cnt=ed=1;}
    }sam_t;
    
    int cnt_ev,ans[MAXN+5];
    struct Event{
    	int l,r,id;
    	Event(){}
    	Event(int _l,int _r,int _id){
    		l=_l;r=_r;id=_id;
    	}
    }ev[MAXN*2+5];
    bool cmp(Event x,Event y){
    	if(x.r==y.r)return x.id<y.id;
    	return x.r<y.r;
    }
    
    struct SegmentTree{
    	int mx[MAXN*4+5];
    	void push_up(int p){
    		mx[p]=max(mx[p<<1],mx[p<<1|1]);
    	}
    	void build(int p,int l,int r,int* arr){
    		if(l==r){
    			mx[p]=arr[l];
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(p<<1,l,mid,arr);
    		build(p<<1|1,mid+1,r,arr);
    		push_up(p);
    	}
    	void modify(int p,int l,int r,int pos,int x){
    		if(l==r){
    			mx[p]=x;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(pos<=mid)modify(p<<1,l,mid,pos,x);
    		else modify(p<<1|1,mid+1,r,pos,x);
    		push_up(p);
    	}
    	int query(int p,int l,int r,int ql,int qr){
    		if(ql<=l && qr>=r)return mx[p];
    		int mid=(l+r)>>1;
    		int res=-INF;
    		if(ql<=mid)res=query(p<<1,l,mid,ql,qr);
    		if(qr>mid)res=max(res,query(p<<1|1,mid+1,r,ql,qr));
    		return res;
    	}
    	SegmentTree(){}
    }T1,T2;
    
    int main() {
    	cin>>(s+1);n=strlen(s+1);
    	cin>>(t+1);m=strlen(t+1);
    	for(int i=1;i<=m;++i){
    		sam_t.ins(t[i]-'a');
    	}
    	int p=1;
    	for(int i=1;i<=n;++i){
    		int r=max(i-1,R[i-1]);
    		if(r==i-1)p=1;
    		while(p!=1 && sam_t.len[sam_t.fa[p]]+1>r-i+1)p=sam_t.fa[p];
    		while(r+1<=n && sam_t.mp[p][s[r+1]-'a']!=0){
    			p=sam_t.mp[p][s[r+1]-'a'];
    			r++;
    		}
    		R[i]=r;
    		ev[++cnt_ev]=Event(i,R[i],0);
    		//cout<<R[i]<<" ";
    	}
    	//cout<<endl;
    	static int tmp[MAXN+5];
    	for(int i=1;i<=n;++i)tmp[i]=-INF;
    	T1.build(1,1,n,tmp);
    	for(int i=1;i<=n;++i)tmp[i]=-i;
    	T2.build(1,1,n,tmp);
    	
    	cin>>q;
    	for(int i=1;i<=q;++i){
    		int l,r;cin>>l>>r;
    		ev[++cnt_ev]=Event(l,r,i);
    	}
    	sort(ev+1,ev+cnt_ev+1,cmp);
    	for(int i=1;i<=cnt_ev;++i){
    		if(ev[i].id==0){
    			T1.modify(1,1,n,ev[i].l,ev[i].r-ev[i].l+1);
    			T2.modify(1,1,n,ev[i].l,-INF);
    		}
    		else{
    			int x=T1.query(1,1,n,ev[i].l,ev[i].r);
    			int y=ev[i].r+T2.query(1,1,n,ev[i].l,ev[i].r)+1;
    			ans[ev[i].id]=max(x,y);
    		}
    	}
    	for(int i=1;i<=q;++i)cout<<ans[i]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    vim快速查找
    一次特别二不兮兮的WebStorm经历
    让docker容器使用主机系统时间(挂入/etc/localtime)
    systemd:在service文件中给Exec传入多个参数
    mongodb数据迁移
    明日边缘;逃出克隆岛
    [C++] 类的所有对象实例共享静态类成员变量
    HTTP长连接
    fqPkzJetPK
    何时使用move
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13158865.html
Copyright © 2011-2022 走看看