「HAOI2018」字串覆蓋 (SAM)(Hash)(倍增)

LOJLOJ傳送門

  • 挺好的題,就是提示得有些明顯,很顯然對 rl2000r-l\ge 2000rl50r-l\le 50 有兩種不同的做法
  • 一個比較容易發現的貪心是我們選儘量前面的匹配,匹配過後再尋找下一個位置
    那麼就有一個做法是對第一個串建 SAMSAM 倍增找到第二個串的結點,線段樹合併 endposendpos,在 endposendpos 裏面找一個 (s+len,t](s+len,t] 的匹配,然後再找下一個,顯然只會匹配 (ts+1)/(rl+1)(t-s+1)/(r-l+1) 次,那麼 rl2000r-l\ge2000 的部分就可以解決掉,單次詢問的複雜度是 log(n)n2000log(n)*\frac{n}{2000}
  • rl50r-l\le 50 :由於匹配次數很多,考慮倍增,我們對每一個長度預處理每一個末尾位置的下一個串以及後 2j2^j 個串的位置,同時求出跳這麼多步的貢獻,由於空間開不下,對詢問離線按長度遞增處理
    預處理可以用 hashhash
    那麼每次可以通過線段樹找到第一個出現位置然後向後跳,單次詢問的複雜度是 O(log(n)2)O(log(n)^2)
    預處理的複雜度是 O(50nlog(n))O(50*n*log(n))

好寫好調 ,只有 5k5k

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt=cnt*10+(ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 50;
typedef long long ll;
int n, q, K, rt[N<<1], lg[N<<1];
namespace SGT{
	cs int N = ::N * 80;
	int ls[N], rs[N], sz[N], nd;
	#define mid ((l+r)>>1)
	void ins(int &x, int l, int r, int p){
		if(!x) x=++nd; ++sz[x]; if(l==r) return;
		(p<=mid)?ins(ls[x],l,mid,p):ins(rs[x],mid+1,r,p);
	}
	int merge(int x, int y){
		if(!x||!y) return x|y; int nx=++nd;
		ls[nx]=merge(ls[x],ls[y]);
		rs[nx]=merge(rs[x],rs[y]); 
		sz[nx]=sz[ls[nx]]+sz[rs[nx]]; return nx;
	}
	int query(int x, int l, int r, int L, int R){
		if(!x) return 0; if(l==r) return l; int as=0;
		if(L<=mid) as=query(ls[x],l,mid,L,R); if(as) return as;
		if(R>mid) as=query(rs[x],mid+1,r,L,R);  return as;
	}
}
namespace SAM{
	cs int N = ::N<<1;
	int ch[N][26], lk[N], len[N], r[N], nd=1, las=1;
	int fa[N][20],dep[N];
	void extend(int c, int k){
		int now=++nd, p=las; r[now]=k; len[now]=len[p]+1;
		for(;p&&!ch[p][c];p=lk[p]) ch[p][c]=now;
		if(!p) lk[now]=1;
		else{
			int q=ch[p][c]; 
			if(len[q]==len[p]+1) lk[now]=q;
			else{
				int cl=++nd; len[cl]=len[p]+1; lk[cl]=lk[q];
				memcpy(ch[cl],ch[q],sizeof(ch[cl]));
				for(;p&&ch[p][c]==q;p=lk[p]) ch[p][c]=cl;
				lk[q]=lk[now]=cl;
			}
		} las=now;
	} 
	void trans(int &u, int &l, int c){
		while(u&&!ch[u][c]) u=lk[u], l=len[u];
		if(!u) u=1, l=0; else u=ch[u][c], ++l;
	}
	void build(){
		static int bin[N], A[N];
		for(int i=1; i<=nd; i++) ++bin[len[i]];
		for(int i=1; i<=n; i++) bin[i]+=bin[i-1];
		for(int i=nd; i>=1; i--) A[bin[len[i]]--]=i;
		for(int i=nd; i>=2; i--){
			int x = A[i]; fa[x][0] = lk[x];
			if(r[x]) SGT::ins(rt[x],1,n,r[x]);
			rt[lk[x]]=SGT::merge(rt[lk[x]],rt[x]);
		} 
		for(int i=1; i<=nd; i++) dep[A[i]]=dep[lk[A[i]]]+1;
		for(int i=1; i<=nd; i++) 
		for(int j=1; j<=lg[dep[A[i]]]; j++)
		fa[A[i]][j]=fa[fa[A[i]][j-1]][j-1];
	}
	int jump(int x, int ln){
		for(int i=lg[dep[x]];~i;i--) 
		if(len[fa[x][i]]>=ln) x=fa[x][i]; return x;
	}
}
char S[N], T[N];
int ps[N], len[N];
namespace Subtask1{
	ll work(int s, int t, int l, int r){
		int x = ps[r], nl = r-l+1; if(len[r] < nl) return 0;
		x = SAM::jump(x, nl); int mat = s; ll as = 0; 
		while(1){
			if(mat+nl-1>t) break;
			int p = SGT::query(rt[x],1,n,mat+nl-1,t);
			if(!p) break; as+=(ll)(K-(p-nl+1)); mat=p+1;
		} return as;
	}
}
namespace Subtask2{
	typedef unsigned long long ull;
	int nxt[N][20]; ll sm[N][20];
	unordered_map<ull, int> mp;
	cs int Base = 5261023;
	ull haS[N], pw[N];
	ull GetS(int l, int r){ return haS[r]-haS[l-1]*pw[r-l+1]; }
	void init(int LEN){
		if(LEN>n) return; mp.clear();
		memset(nxt,0,sizeof(nxt));
		memset(sm,0,sizeof(sm));
		for(int i=n; i>=LEN; i--){
			if(i+LEN<=n) mp[GetS(i+1,i+LEN)]=i+LEN;
			nxt[i][0] = mp[GetS(i-LEN+1,i)];
			sm[i][0] = K-(i-LEN+1);
		}
		for(int i=n; i>=1; i--)
		for(int j=1; j<=lg[(n-i+1)/LEN]; j++){
			nxt[i][j]=nxt[nxt[i][j-1]][j-1];
			sm[i][j]=sm[i][j-1]+sm[nxt[i][j-1]][j-1];
		} 
	}
	void prework(){ 
		pw[0]=1; for(int i=1; i<=n; i++) pw[i]=pw[i-1]*Base;
		for(int i=1; i<=n; i++) haS[i]=haS[i-1]*Base+S[i];
	}
	ll work(int s, int t, int l, int r){
		int x = ps[r], LEN = r-l+1; if(len[r] < LEN) return 0;
		x = SAM::jump(x,LEN); int u=SGT::query(rt[x],1,n,s+LEN-1,t);
		if(!u) return 0; ll as=0;
		for(int j=lg[(n-u+1)/LEN];~j;j--)
		if(nxt[u][j]&&nxt[u][j]<=t) as+=sm[u][j], u=nxt[u][j];
		as+=(ll)K-(u-LEN+1);
		return as; 
	}
}
struct qry{ int s,t,l,r,c; }qr[N];
bool cmp(qry a, qry b){ return (a.r-a.l) < (b.r-b.l); }
int main(){
	n = read(), K = read();
	for(int i=2; i<=n+n; i++) lg[i]=lg[i>>1]+1;
	scanf("%s",S+1);
	scanf("%s",T+1);
	for(int i=1; i<=n; i++) SAM::extend(S[i]-'a',i); 
	ps[0]=1; len[0]=0;
	for(int i=1; i<=n; i++) 
	ps[i]=ps[i-1], len[i]=len[i-1], SAM::trans(ps[i],len[i],T[i]-'a');
	SAM::build();
	Subtask2::prework();
	q = read();
	for(int i=1; i<=q; i++){
		int s=read(), t=read(), l=read(), r=read();
		qr[i] = (qry){s,t,l,r,i};
	} 
	sort(qr+1, qr+q+1, cmp);
	static ll as[N];
	for(int i=1,las=0; i<=q; i++){
		int s=qr[i].s, t=qr[i].t, l=qr[i].l, r=qr[i].r;
		if(r-l+1<=50 && r-l+1!=las) Subtask2::init(r-l+1), las=r-l+1;
		if(r-l+1<=50) as[qr[i].c]=Subtask2::work(s,t,l,r);
		else as[qr[i].c]=Subtask1::work(s,t,l,r);
	} 
	for(int i=1; i<=q; i++) cout<<as[i]<<'\n';
	return 0;
} 
發佈了651 篇原創文章 · 獲贊 98 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章