【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

时间:2023-02-16 11:19:59

【BZOJ5304】[HAOI2018]字串覆盖(后缀数组,主席树,倍增)

题面

BZOJ

洛谷

题解

贪心的想法是从左往右,能选就选。这个显然是正确的。

题目的数据范围很好的说明了要对于询问分开进行处理。

先考虑询问的模板串长比较大的情况。

那么只需要每次找到一个范围内的最小位置然后接着暴力跳就可以了。

这个这个过程可以把\(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]字串覆盖(后缀数组,主席树,倍增)的更多相关文章

  1. LOJ&lowbar;&num;2720&period; 「NOI2018」你的名字 &lowbar;后缀数组&plus;主席树&plus;倍增

    题面: https://loj.ac/problem/2720 考虑枚举T串的每个后缀i,我们要做两件事. 一.统计有多少子串[i,j]在S中要求位置出现. 二.去重. 第二步好做,相当于在后缀数组上 ...

  2. BZOJ5304 &colon; &lbrack;Haoi2018&rsqb;字串覆盖

    离线处理所有询问. 对于$r-l\leq 50$的情况: 按照串长从$1$到$51$分别把所有子串按照第一位字符为第一关键字,上一次排序结果为第二关键字进行$O(n)$基数排序. 同理也可以用上一次比 ...

  3. 洛谷P4493 &lbrack;HAOI2018&rsqb;字串覆盖(后缀自动机&plus;线段树&plus;倍增)

    题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...

  4. 洛谷P2408 不同字串个数 &lbrack;后缀数组&rsqb;

    题目传送门 不同字串个数 题目背景 因为NOI被虐傻了,蒟蒻的YJQ准备来学习一下字符串,于是它碰到了这样一道题: 题目描述 给你一个长为N的字符串,求不同的子串的个数 我们定义两个子串不同,当且仅当 ...

  5. &lbrack;BZOJ4556&rsqb;&lbrack;Tjoi2016&amp&semi;Heoi2016&rsqb;字符串 后缀数组&plus;主席树

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Description 佳媛姐姐过生日的时候,她的小 ...

  6. &lbrack;2019CCPC网络赛&rsqb;&lbrack;hdu6704&rsqb;K-th occurrence&lpar;后缀数组&amp&semi;&amp&semi;主席树&rpar;

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6704 题意为查询子串s[l...r]第k次出现的位置. 写完博客后5分钟的更新 写完博客才发现这份代码 ...

  7. HDU-6704 K-th occurrence(后缀数组&plus;主席树)

    题意 给一个长度为n的字符串,Q次询问,每次询问\((l,r,k)\) , 回答子串\(s_ls_{l+1}\cdots s_r\) 第\(k\) 次出现的位置,若不存在输出-1.\(n\le 1e5 ...

  8. BZOJ3473&colon;字符串&lpar;后缀数组&comma;主席树&comma;二分&comma;ST表&rpar;

    Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 一 ...

  9. P5346 【XR-1】柯南家族&lpar;后缀数组&plus;主席树&rpar;

    题目 P5346 [XR-1]柯南家族 做法 聪明性是具有传递性的,且排列是固定的 那么先预处理出每个点的名次,用主席树维护\(k\)大值 一眼平衡树,遍历的同时插入\(O(log^2n)\),总时间 ...

随机推荐

  1. Bubble Cup 8 finals C&period; Party &lpar;575C&rpar;

    题意: 给定n个人,分两天晚上去夜总会开派对,要求每天恰好有n/2个人去,且每人去的夜总会各不相同. 每个人对不同的晚上不同的夜总会有不同的满意度,求一个方案使得所有人的满意度之和最大. 夜总会数量= ...

  2. javascript中的对象

    除了字符串,数字,布尔值(true,false),null,undefined,js中的值都是对象. 操作一个对象 var o = {name: 'man', value: 99} o.name = ...

  3. C&plus;&plus;中的数组越界

    C++中数组发生越界错误时, compiling过程不会报错, linking过程也不会报错, 会在executing过程中发生意想不到的错误或问题.

  4. a标签与click的关系

    当点击浏览器a标签的时候,浏览器的默认机制如下: 1.触发a的click事件2.读取href属性的值3.如果是URI则跳转4.如果是javascript代码则执行该代码 下面我们一起来做一个实验: 我 ...

  5. 在R语言中无法设置CRAN镜像问题

    很大的可能是因为使用的浏览器不是IE浏览器的问题,因为CRAN的镜像需要用IE浏览器来打开. 只需要按照下面设置即可: 1.打开IE-->设置-->Internet选项-->高级 2 ...

  6. hdu-----2491Priest John&&num;39&semi;s Busiest Day(2008 北京现场赛G&rpar;

    Priest John's Busiest Day Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Jav ...

  7. 更快的理解js中循环嵌套

    [循环控制语句] break语句:终止本层循环,继续执行循环后面的语句:(当循环有多层时,break只会跳出一层循环) continue语句:跳过本次循环,继续执行下次循环: (对于for循环,con ...

  8. Opencv 3&period;3&period;0 常用函数

    如何调图像的亮度和对比度? //如何增加图片的对比度或亮度? void contrastOrBrightAdjust(InputArray &src,OutputArray &dst, ...

  9. &lbrack;PHP&rsqb; 算法-将一个字符串转换成一个整数的PHP实现

    题目描述 将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一 ...

  10. mysql 提示表损坏处理方法

     公司网站有些页面打不开,第二次出现这个情况 重启数据库,提示有一个表crashed:这是第二次出现这个问题,这个时候,进入数据库文件目录输入:myisamchk -r "Table_Nam ...