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的更多相关文章

  1. 【uva10829-求形如UVU的串的个数】后缀数组+rmq or 直接for水过

    题意:UVU形式的串的个数,V的长度规定,U要一样,位置不同即为不同字串 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&am ...

  2. 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 ...

  3. PHP 中替换若干字符串字串为数组中的值,不用循环,非常高效

    替换某个字符串中的一个或若干个字串为数组中某些值 php本身有自带的函数,可以不用循环非常高效的实现其效果: 实例代码:   $phrase  = "You should eat fruit ...

  4. POJ 3693 后缀数组+RMQ

    思路: 论文题 后缀数组&RMQ 有一些题解写得很繁 //By SiriusRen #include <cmath> #include <cstdio> #includ ...

  5. BZOJ 3277: 串/ BZOJ 3473: 字符串 ( 后缀数组 + RMQ + 二分 )

    CF原题(http://codeforces.com/blog/entry/4849, 204E), CF的解法是O(Nlog^2N)的..记某个字符串以第i位开头的字符串对答案的贡献f(i), 那么 ...

  6. HDU2459 后缀数组+RMQ

    题目大意: 在原串中找到一个拥有连续相同子串最多的那个子串 比如dababababc中的abababab有4个连续的ab,是最多的 如果有同样多的输出字典序最小的那个 这里用后缀数组解决问题: 枚举连 ...

  7. hdu 2459 (后缀数组+RMQ)

    题意:让你求一个串中连续重复次数最多的串(不重叠),如果重复的次数一样多的话就输出字典序小的那一串. 分析:有一道比这个简单一些的题spoj 687, 假设一个长度为l的子串重复出现两次,那么它必然会 ...

  8. ural 1297(后缀数组+RMQ)

    题意:就是让你求一个字符串中的最长回文,如果有多个长度相等的最长回文,那就输出第一个最长回文. 思路:这是后缀数组的一种常见的应用,首先把原始字符串倒转过来,然后接在原始字符串的后面,中间用一个不可能 ...

  9. 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)

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

随机推荐

  1. Qt-网易云音乐界面实现-2 红红的程序运行图标,和相似下方音乐条

    被调出来出差了,这次出差可以说是非常不开心,这次出差也算给我自己提了个醒吧,那就是注意自己的精力,自己的口碑,和比人对自己的信任.具体内容如下 我们公司有一款硬件的设备的电路是外包给某个人来做的,这个 ...

  2. Java创建对象的动作分析

    一.Java创建对象时将对象存放到内存的堆中. 创建对象时先执行类成员的初始化,然后才会调用构造函数初始化对象, package com.test.createsort; public class C ...

  3. 基于Python的信用评分卡模型分析(一)

    信用风险计量体系包括主体评级模型和债项评级两部分.主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡.B卡.C卡和F卡:债项评级模型通常按照主体的融资用途,分为 ...

  4. ROS (Robot Operating System) 相关资料与文档

    本博文持续更新…… ROS学习与实践保护很多资料,最权威的当然是官网: www.ros.org 当然也有很多其他博文,尤其是中文文档有助于理解.2017推出一篇新博文,概况的资料较全. http:// ...

  5. Leetcode_6. Zigzag convertion

    6. Zigzag convertion 对输入的字符串做锯齿形变换,并输出新的字符串,所谓zigzag变化如下图所示. 将"ABCDEFGHIJKL"做4行的锯齿变换,新的字符串 ...

  6. redis使用哈希槽实现集群

    Redis Cluster集群 一.redis-cluster设计 Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Re ...

  7. 互评Beta版本——杨老师粉丝群——Pinball

    互评beta版本    杨老师粉丝群——<PinBall> 一.基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 随着年龄的增长, ...

  8. 20172308 实验一《Java开发环境的熟悉》实验报告

    20172308 2017-2018-2 <程序设计与数据结构>实验1报告 课程:<程序设计与数据结构> 班级: 1723 姓名: 周亚杰 学号:20172308 实验教师:王 ...

  9. 第一阶段Spring个人总结

    通过这一阶段的冲刺,我感到的是名义上的团队,而实际上却是一个人的事,每个人跟每个人都不一样,都有自己的特点,总会出些不必要的麻烦. 还有团队的进展也是看不到什么东西,说实话,这次我并没有太多关注团队的 ...

  10. 调研Android的开发环境的发展演变

    在 知道要做基于移动端的项目实践时,我就选定了Android,回来的时候查了很多相关的知识,很多人都在问开发安卓软件,使用eclipse还是用 Android studio?其实,也没有一个准确的答案 ...