LOJ6031 字符串

字符串

\(s\)\(w\)为两字符串,定义:

  1. \(w[l, r]\)表示字符串\(w\)在区间\([l, r]\)中的子串;
  2. \(w\)\(s\)中出现的频率定义为\(w\)\(s\)中出现的次数;
  3. \(f(s, w, l, r)\)表示\(w[l, r]\)\(s\)中出现的频率。

现在给定串\(s\)\(m\)个区间\([l, r]\)和长度\(k\),你要回答\(q\)个询问,每个询问给你一个长度为\(k\)的字符串\(w\)和两个整数\(a, b\),求:

\[\sum\limits_{i = a} ^ b f(s, w, l_i, r_i) \]

对于\(100\%\)的数据,满足\(n, m, k, q \leq 10 ^ 5, \sum w \leq 10 ^ 5\),字符串由小写英文字母构成。

题解

SAM + 根号分治的奇怪组合。

http://jklover.hs-blog.cf/2020/04/16/Loj-6031-字符串/

注意到这题不好做的地方在于:虽然串长有保证,但是每次的询问可能有很多。

  • \(k\leq q\)时,暴力枚举子串。

    可以将\([l,r]\)\(m\)个区间中出现的所有位置存在vector中,二分找出\(a,b\)的位置,做差即为所求。

  • \(k>q\)时,暴力枚举询问。

    就用经典的SAM定位法+倍增找祖先即可。

时间复杂度\(O(q(k+m\log n))=O(p+m\sqrt p\log n)\)

CO int N=2e5+10;
int last=1,tot=1;
array<int,26> ch[N];
int fa[N],len[N],siz[N];

void extend(int c){
	int x=last,cur=last=++tot;
	len[cur]=len[x]+1,siz[cur]=1;
	for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
	if(!x) {fa[cur]=1; return;}
	int y=ch[x][c];
	if(len[y]==len[x]+1) {fa[cur]=y; return;}
	int clone=++tot;
	ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
	fa[cur]=fa[y]=clone;
	for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
}

int cnt[N],ord[N];

void toposort(){
	for(int i=1;i<=tot;++i) ++cnt[len[i]];
	for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
	for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
	for(int i=tot;i>=2;--i) siz[fa[ord[i]]]+=siz[ord[i]];
}

char s[N],w[N];
int n,m,q,k,L[N],R[N];

namespace Task1{
	CO int K=320;
	vector<int> vec[K][K];
	
	IN int calc(CO vector<int>&vec,int a,int b){
		return upper_bound(vec.begin(),vec.end(),b)-lower_bound(vec.begin(),vec.end(),a);
	}
	void main(){
		for(int i=1;i<=m;++i)
			if(1<=L[i] and L[i]<=R[i] and R[i]<=k)
				vec[L[i]][R[i]].push_back(i);
		while(q--){
			scanf("%s",w+1);
			int a=read<int>()+1,b=read<int>()+1;
			int64 ans=0;
			for(int l=1;l<=k;++l){
				int x=1;
				for(int r=l;r<=k;++r){
					x=ch[x][w[r]-'a'];
					if(!x) break;
					if(vec[l][r].empty()) continue;
					if(vec[l][r].back()<a or vec[l][r].front()>b) continue;
					ans+=(int64)siz[x]*calc(vec[l][r],a,b);
				}
			}
			printf("%lld\n",ans);
		}
	}
}

namespace Task2{
	int anc[N][18];
	vector<int> vec[N];
	
	int query(int x,int lim){
		if(len[x]<lim) return 0;
		for(int i=17;i>=0;--i)
			if(len[anc[x][i]]>=lim) x=anc[x][i];
		return siz[x];
	}
	void main(){
		for(int i=2;i<=tot;++i){
			int x=ord[i];
			anc[x][0]=fa[x];
			for(int j=1;j<=17;++j) anc[x][j]=anc[anc[x][j-1]][j-1];
		}
		while(q--){
			scanf("%s",w+1);
			int a=read<int>()+1,b=read<int>()+1;
			for(int i=a;i<=b;++i) vec[R[i]].push_back(L[i]);
			int64 ans=0;
			int x=1,l=0;
			for(int r=1;r<=k;++r){
				int c=w[r]-'a';
				if(ch[x][c]) x=ch[x][c],++l;
				else{
					while(x and !ch[x][c]) x=fa[x];
					if(!x) x=1,l=0;
					else l=len[x]+1,x=ch[x][c]; // important
				}
				for(int i:vec[r])
					if(l>=r-i+1) ans+=query(x,r-i+1);
			}
			printf("%lld\n",ans);
			for(int i=a;i<=b;++i) vec[R[i]].clear();
		}
	}
}

int main(){
	read(n),read(m),read(q),read(k);
	scanf("%s",s+1);
	for(int i=1;i<=n;++i) extend(s[i]-'a');
	toposort();
	for(int i=1;i<=m;++i) L[i]=read<int>()+1,R[i]=read<int>()+1;
	if(k<=q) Task1::main();
	else Task2::main();
	return 0;
}

posted on 2020-04-19 16:12  autoint  阅读(165)  评论(0编辑  收藏  举报

导航