【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)
题面
题解
贪心的想法是从左往右,能选就选。这个显然是正确的。
题目的数据范围很好的说明了要对于询问分开进行处理。
先考虑询问的模板串长比较大的情况。
那么只需要每次找到一个范围内的最小位置然后接着暴力跳就可以了。
这个这个过程可以把\(AB\)两个串拼接在一起求\(SA\),这样能够匹配上\(P\)串的\(A\)的后缀的起始位置在\(SA\)上就是一段连续区间。考虑每次找出在\(A\)的\([l,r]\)范围内的合法的最小值,那么对于后缀数组的每个位置对应的\(A\)上的位置构建主席树,这样子每次就可以在主席树上进行二分来寻找到第一个合法的位置。这样一来反复横跳就可以得到匹配的答案。
这样子的时间复杂度是\(O(n/|P|*logn)\),当给定串长比较大的时候是可以接受的。
但是当\(|P|\)很小的时候显然不能暴跳,因此肯定需要预处理答案。
对于每个\(len\)进行预处理,显然每个合法的串的起始位置都可以找到唯一的一个最小的合法位置(或者不存在),满足以这两个位置为起始位置往后匹配\(len\)位相等,并且两个长度为\(len\)的串不交。
显然这样子的结构构成了一棵树,那么把树给构出来之后,对于小于特定值的询问在树上倍增计算即可。
根据题目数据范围显然两种方法的分界线是\(50\)。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define MAX 200100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,K,m,lg[MAX];
char A[MAX];
struct SuffixArray
{
	int hg[20][MAX],SA[MAX],rk[MAX];
	int a[MAX],t[MAX],x[MAX],y[MAX];
	bool cmp(int i,int j,int k){return y[i]==y[j]&&y[i+k]==y[j+k];}
	void GetSA()
	{
		int m=255;
		for(int i=1;i<=n;++i)t[x[i]=a[i]]+=1;
		for(int i=1;i<=m;++i)t[i]+=t[i-1];
		for(int i=n;i>=1;--i)SA[t[x[i]]--]=i;
		for(int k=1;k<=n;k<<=1)
		{
			int p=0;
			for(int i=n-k+1;i<=n;++i)y[++p]=i;
			for(int i=1;i<=n;++i)if(SA[i]>k)y[++p]=SA[i]-k;
			for(int i=1;i<=m;++i)t[i]=0;
			for(int i=1;i<=n;++i)t[x[y[i]]]+=1;
			for(int i=1;i<=m;++i)t[i]+=t[i-1];
			for(int i=n;i>=1;--i)SA[t[x[y[i]]]--]=y[i];
			swap(x,y);x[SA[1]]=p=1;
			for(int i=2;i<=n;++i)x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
			if(p>=n)break;m=p;
		}
		for(int i=1;i<=n;++i)rk[SA[i]]=i;
		for(int i=1,j=0;i<=n;++i)
		{
			if(j)--j;
			while(a[i+j]==a[SA[rk[i]+1]+j])++j;
			hg[0][rk[i]]=j;
		}
		for(int j=1;j<=lg[n];++j)
			for(int i=1;i+(1<<j)-1<=n;++i)
				hg[j][i]=min(hg[j-1][i],hg[j-1][i+(1<<(j-1))]);
	}
}SA;
struct SegmentTree
{
	struct Node{int ls,rs,v;}t[MAX*20];
	int tot;
	void Modify(int &x,int l,int r,int p)
	{
		t[++tot]=t[x];x=tot;t[x].v+=1;
		if(l==r)return;int mid=(l+r)>>1;
		if(p<=mid)Modify(t[x].ls,l,mid,p);
		else Modify(t[x].rs,mid+1,r,p);
	}
	int Query(int x,int y,int l,int r,int p)
	{
		if(!(t[y].v-t[x].v))return 0;
		if(l==r)return l;
		int mid=(l+r)>>1,ret=0;
		if(p<=mid)ret=Query(t[x].ls,t[y].ls,l,mid,p);
		if(!ret)ret=Query(t[x].rs,t[y].rs,mid+1,r,p);
		return ret;
	}
}T;
int rt[MAX];ll ans[MAX];
struct Qry{int s,t,l,len,pos,id;}Q[MAX];
bool operator<(Qry a,Qry b){return a.len!=b.len?a.len<b.len:a.pos<b.pos;}
int fa[20][MAX];ll sum[20][MAX];
void Work(int len,int &L)
{
	static int pos[MAX];
	for(int l=1,r=l;l<=n+n+1;r=l=r+1)
	{
		if(Q[L].len!=len)return;
		int cnt=0;
		while(SA.hg[0][r]>=len)++r;
		for(int j=l;j<=r;++j)if(SA.SA[j]<=n)pos[++cnt]=SA.SA[j];
		if(Q[L].pos<l||Q[L].pos>r)continue;
		sort(&pos[1],&pos[cnt+1]);pos[cnt+1]=n+n+1;
		int k=1;
		for(int i=1;i<=cnt;++i)
		{
			while(pos[k]-pos[i]<len)++k;
			fa[0][i]=k;sum[0][i]=K-pos[i];
		}
		int lim=lg[min(cnt+1,n/len)];
		for(int j=1;j<=lim;++j)
			for(int i=1;i<=cnt;++i)
				fa[j][i]=fa[j-1][fa[j-1][i]],sum[j][i]=sum[j-1][i]+sum[j-1][fa[j-1][i]];
		while(Q[L].len==len&&l<=Q[L].pos&&Q[L].pos<=r)
		{
			int s=Q[L].s,t=Q[L].t,x=lower_bound(&pos[1],&pos[cnt+2],s)-pos;
			if(pos[x]<=t)
			{
				int u=x;ll ret=0;
				for(int i=lim;~i;--i)
					if(pos[fa[i][u]]&&pos[fa[i][u]]<=t)
						ret+=sum[i][u],u=fa[i][u];
				ans[Q[L].id]=ret+sum[0][u];
			}
			++L;
		}
		for(int i=0;i<=lim;++i)
			for(int j=1;j<=cnt+1;++j)
				fa[i][j]=sum[i][j]=0;
	}
}
int main()
{
	n=read();K=read();
	scanf("%s",A+1);A[n+1]='#';scanf("%s",A+n+2);
	n=n+n+1;for(int i=1;i<=n;++i)SA.a[i]=A[i];
	for(int i=2;i<=n;++i)lg[i]=lg[i>>1]+1;
	SA.GetSA();n>>=1;
	m=read();
	for(int i=1;i<=m;++i)
	{
		int s=read(),t=read(),l=read(),r=read();
		Q[i]=(Qry){s,t-(r-l),l,r-l+1,SA.rk[n+1+l],i};
	}
	sort(&Q[1],&Q[m+1]);
	int pos=1;
	for(int i=1;i<=n&&i<=50;++i)Work(i,pos);
	for(int i=1;i<=n+n+1;++i)
		if(SA.SA[i]<=n)T.Modify(rt[i]=rt[i-1],1,n,SA.SA[i]);
		else rt[i]=rt[i-1];
	for(;pos<=m;++pos)
	{
		int s=Q[pos].s,t=Q[pos].t,len=Q[pos].len;
		int x=Q[pos].pos,l=x,r=x;
		for(int i=lg[n]+1;~i;--i)
			if(l-(1<<i)>0&&SA.hg[i][l-(1<<i)]>=len)l-=1<<i;
		for(int i=lg[n]+1;~i;--i)
			if(r+(1<<i)<=n+n+1&&SA.hg[i][r]>=len)r+=1<<i;
		for(int u=s,v;u<=n;u=v+len)
		{
			v=T.Query(rt[l-1],rt[r],1,n,u);
			if(v>t||!v)break;
			ans[Q[pos].id]+=K-v;
		}
	}
	for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
	return 0;
}
【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)的更多相关文章
- LOJ_#2720. 「NOI2018」你的名字 _后缀数组+主席树+倍增
		题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ... 
- BZOJ5304 : [Haoi2018]字串覆盖
		离线处理所有询问. 对于$r-l\leq 50$的情况: 按照串长从$1$到$51$分别把所有子串按照第一位字符为第一关键字,上一次排序结果为第二关键字进行$O(n)$基数排序. 同理也可以用上一次比 ... 
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
		题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ... 
- 洛谷P2408 不同字串个数 [后缀数组]
		题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ... 
- [BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
		4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ... 
- [2019CCPC网络赛][hdu6704]K-th occurrence(后缀数组&&主席树)
		题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6704 题意为查询子串s[l...r]第k次出现的位置. 写完博客后5分钟的更新 写完博客才发现这份代码 ... 
- HDU-6704 K-th occurrence(后缀数组+主席树)
		题意 给一个长度为n的字符串,Q次询问,每次询问\((l,r,k)\) , 回答子串\(s_ls_{l+1}\cdots s_r\) 第\(k\) 次出现的位置,若不存在输出-1.\(n\le 1e5 ... 
- BZOJ3473:字符串(后缀数组,主席树,二分,ST表)
		Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ... 
- P5346 【XR-1】柯南家族(后缀数组+主席树)
		题目 P5346 [XR-1]柯南家族 做法 聪明性是具有传递性的,且排列是固定的 那么先预处理出每个点的名次,用主席树维护\(k\)大值 一眼平衡树,遍历的同时插入\(O(log^2n)\),总时间 ... 
随机推荐
- elasticsearch5.0版本的head安装
			elasticsearch5.0版本的head安装 elasticsearch5.0版本由于刚出不久,并且与2.0版本的差距较大.所以,目前大家对5.0的一些使用还有所陌生.这里先把关于head插件的 ... 
- linux中根据名称kill进程
			shell函数如下: # kill processes by name kbn() { line=`ps -a | grep $1` arr=($line) for((i=0;i<${#arr[ ... 
- python_format格式化输出、while else、逻辑运算符、编码初识
			1.格式化输出 .%d %s 格式化输出:% 占位符,d 表示替换整型数,s表示要替换字符串. name = input('请输入名字:') age = input('请输入年龄:') sex = ... 
- Vue基本使用和指令集
			Vue的使用 一.安装 对于新手来说,强烈建议大家使用<script>引入: 二. 引入vue.js文件 我们能发现,引入vue.js文件之后,Vue被注册为一个全局的变量,它是一个构造函 ... 
- 访问修饰符 public private protected default
- tomcat  one connection one thread one request one thread
			java - What is the difference between thread per connection vs thread per request? - Stack Overflow ... 
- react组件传值传方法
			<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- 逻辑斯特回归tensorflow实现
			calss #!/usr/bin/python2.7 #coding:utf-8 from __future__ import print_function import tensorflow as ... 
- oracle计算时间常用函数
			--ddd:一年中的第几天 select to_char(sysdate,'ddd') from dual --d:一周中的第几天 星期天是第一天 所以要-1select to_char(sysdat ... 
- laravel添加model文件夹,需要改动的地方
			首先,将app\User(等model文件),移入APP\modellists文件夹中,方便整理 第二,修改模型中命名空间和引用其他model的路径 第三,将文件夹app\admin中的控制器文件,全 ... 
