[BZOJ3230] 相似字串 后缀数组+RMQ
3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MB
Description
Input
输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。
Output
输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。
Sample Input
5 3
ababa
3 5
5 9
8 10
Sample Output
18
16
-1
HINT
样例解释
第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。
第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。
第3组询问:不存在第10个子串。输出-1。数据范围
N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成
题解: 首先我们要解决的是本质不同的子串计数问题:
考虑到子串一定是后缀的前缀,我们按照rank顺序添加每个后缀,
那么每添加一个新后缀就会产生n-sa+1个新的前缀(子串)
但是由于lcp的存在,这些子串又和前面的那一串重复的一些
所以最后的计算式是Σn-sa+1-height,当然具体的细节,诸如±1会随代码风格和计算方式略有不同,读者自行修改即可
接着我们考虑,本题其实就是让我们求某两个子串最长公共前缀和最长公共后缀
这样我们可以跑一个SA之后把字串反转再求一套,我们就得到了后缀数组和一个诡异的"前缀数组"
接着我们二分找到子串对应的端点以及后缀,再用rmq求一下lcp区间最小值即可.
代码实现(当时我调到意识模糊于是封装了一下233):
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=;
int n,xx[N],yy[N],cnt[N],bin[];
LL num[N];
struct Fleet
{
int sa[N],height[N],rank[N],f[N][];
char s[N];
int i,j,k,p,m;
inline void get_sa()
{
int *x=xx,*y=yy;m=;
for(i=;i<m;++i)cnt[i]=;
for(i=;i<n;++i)++cnt[x[i]=s[i]];
for(i=;i<m;++i)cnt[i]+=cnt[i-];
for(i=n-;~i;--i)sa[--cnt[x[i]]]=i;
for(k=,p=;p<n&&k<=n;k<<=,m=p)
{
for(p=,i=n-k;i<n;++i)y[p++]=i;
for(i=;i<n;++i)if(sa[i]>=k)y[p++]=sa[i]-k;
for(i=;i<m;++i)cnt[i]=;
for(i=;i<n;++i)++cnt[x[y[i]]];
for(i=;i<m;++i)cnt[i]+=cnt[i-];
for(i=n-;~i;--i)sa[--cnt[x[y[i]]]]=y[i];
for(swap(x,y),p=,x[sa[]]=,i=;i<n;++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-]]&&y[sa[i]+k]==y[sa[i-]+k])?p-:p++;
}
}
inline void get_rank()
{
for(i=;i<n;++i)rank[sa[i]]=i;
for(k=i=;i<n;height[rank[i++]]=k)
for(k=k?k-:k,j=sa[rank[i]-];s[i+k]==s[j+k];++k);
}
inline void ST()
{
for(i=;i<n;++i)f[i][]=height[i];
for(i=;bin[i]<=n;++i)
for(j=;j+bin[i]-<n;++j)
f[j][i]=min(f[j][i-],f[j+bin[i-]][i-]);
}
inline LL query(int l,int r)
{
int len=r-l+,k=;
while(bin[k+]<=len)++k;
return min(f[l][k],f[r-bin[k]+][k]);
}
inline void get_kth(int &st,int &ed,LL rk)
{
int ans=lower_bound(num,num+n,rk)-num;
st=sa[ans],ed=st+height[ans]-+rk-num[ans-];
}
inline void intn()
{get_sa(),get_rank(),ST();}
inline void calc()
{for(i=;i<n;++i)num[i]=num[i-]+LL(n-sa[i]-height[i]-);}
}Sfx,Pre;
inline LL get_length(LL id1,LL id2)
{
register int i,j,k,st[],ed[];
Sfx.get_kth(st[],ed[],id1);
Sfx.get_kth(st[],ed[],id2);
LL val=min(ed[]+1ll-st[],ed[]+1ll-st[]),tmp=val;
if(st[]!=st[])
if(Sfx.rank[st[]]<Sfx.rank[st[]])
tmp=min(tmp,Sfx.query(Sfx.rank[st[]]+,Sfx.rank[st[]]));
else
tmp=min(tmp,Sfx.query(Sfx.rank[st[]]+,Sfx.rank[st[]]));
ed[]=n--ed[],ed[]=n--ed[];
if(ed[]!=ed[])
if(Pre.rank[ed[]]<Pre.rank[ed[]])
val=min(val,Pre.query(Pre.rank[ed[]]+,Pre.rank[ed[]]));
else
val=min(val,Pre.query(Pre.rank[ed[]]+,Pre.rank[ed[]]));
return tmp*tmp+val*val;
}
int main()
{
register int i,j,m,a,b,q;LL u,v;
scanf("%d%d%s",&n,&m,Sfx.s);
for(bin[]=i=;i<=;++i)bin[i]=bin[i-]<<;
for(i=;i<=n;++i)Pre.s[i-]=Sfx.s[n-i];
Sfx.s[n]=Pre.s[n]=,n++;
Sfx.intn();Pre.intn();Sfx.calc();
while(m--)
{
scanf("%lld%lld",&u,&v);
if(u>num[n-]||v>num[n-])printf("-1\n");
else printf("%lld\n",get_length(u,v));
}
}
[BZOJ3230] 相似字串 后缀数组+RMQ的更多相关文章
- 【uva10829-求形如UVU的串的个数】后缀数组+rmq or 直接for水过
题意:UVU形式的串的个数,V的长度规定,U要一样,位置不同即为不同字串 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&am ...
- spoj687 REPEATS - Repeats (后缀数组+rmq)
A string s is called an (k,l)-repeat if s is obtained by concatenating k>=1 times some seed strin ...
- PHP 中替换若干字符串字串为数组中的值,不用循环,非常高效
替换某个字符串中的一个或若干个字串为数组中某些值 php本身有自带的函数,可以不用循环非常高效的实现其效果: 实例代码: $phrase = "You should eat fruit ...
- POJ 3693 后缀数组+RMQ
思路: 论文题 后缀数组&RMQ 有一些题解写得很繁 //By SiriusRen #include <cmath> #include <cstdio> #includ ...
- BZOJ 3277: 串/ BZOJ 3473: 字符串 ( 后缀数组 + RMQ + 二分 )
CF原题(http://codeforces.com/blog/entry/4849, 204E), CF的解法是O(Nlog^2N)的..记某个字符串以第i位开头的字符串对答案的贡献f(i), 那么 ...
- HDU2459 后缀数组+RMQ
题目大意: 在原串中找到一个拥有连续相同子串最多的那个子串 比如dababababc中的abababab有4个连续的ab,是最多的 如果有同样多的输出字典序最小的那个 这里用后缀数组解决问题: 枚举连 ...
- hdu 2459 (后缀数组+RMQ)
题意:让你求一个串中连续重复次数最多的串(不重叠),如果重复的次数一样多的话就输出字典序小的那一串. 分析:有一道比这个简单一些的题spoj 687, 假设一个长度为l的子串重复出现两次,那么它必然会 ...
- ural 1297(后缀数组+RMQ)
题意:就是让你求一个字符串中的最长回文,如果有多个长度相等的最长回文,那就输出第一个最长回文. 思路:这是后缀数组的一种常见的应用,首先把原始字符串倒转过来,然后接在原始字符串的后面,中间用一个不可能 ...
- 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)
3473: 字符串 Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串 ...
随机推荐
- 查找linux镜像源中的软件版本并进行安装
输入以下代码进行软件查找 sudo apt-cache search YourSoftwareName 根据所得到的结果进行安装 sudo apt-get install YourSoftwareNa ...
- 旧的 .NET Core 项目重新打包出现提示版本不对问题
错误提示 当电脑更新 VS2017 版本后,如果同时有新的 .NET Core SDK 更新,打开旧的项目重新打包,可能会报这样的错误 NETSDK1061: 项目是使用 Microsoft.NETC ...
- 10分钟了解 pandas - pandas官方文档译文 [原创]
10 Minutes to pandas 英文原文:https://pandas.pydata.org/pandas-docs/stable/10min.html 版本:pandas 0.23.4 采 ...
- Netty源码分析第5章(ByteBuf)---->第2节: ByteBuf的分类
Netty源码分析第五章: ByteBuf 第二节: ByteBuf的分类 上一小节简单介绍了AbstractByteBuf这个抽象类, 这一小节对其子类的分类做一个简单的介绍 ByteBuf根据不同 ...
- 新手Python第四天(生成器)
Python 生成器 生成器和生成表达式 a=[i*2 for i in range(10)]#生成表达式 b=(i*2 for i in range(10))#生成器 生成器的特点:优点(不占用内存 ...
- RabbitMQ理论部分
概念 queue 队列 exchange 交换机 bind 绑定 channel 通道 一个发送消息流程包含上述四个概念.消息经过channel传递给exc ...
- 软件工程-东北师大站-第十一次作业(PSP)
PSP 饼状图 2.本周进度条 3.本周累计进度图 代码累计折线图 博文字数累计折线图
- 2018-2019-20172321 《Java软件结构与数据结构》第七周学习总结
2018-2019-20172321 <Java软件结构与数据结构>第七周学习总结 教材学习内容总结 第11章 二叉查找树 一.概述 二叉查找树是一种含有附加属性的二叉树,该属性即其左孩子 ...
- EF三种编程方式详细图文教程(C#+EF)之Model First
Model First Model First我们称之为“模型优先”,这里的模型指的是“ADO.NET Entity Framework Data Model”,此时你的应用并没有设计相关数据库,在V ...
- spring冲刺第八天
昨天使人物成功的在地图上运动,并设计炸弹爆炸效果. 今天使炸弹可以炸死人物并可以炸没砖块,并试着将小怪加入地图. 遇到的问题:现在还没有将小怪加入地图,各个模块的整合是比较麻烦的,我还要在这方面下点功 ...