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. bintray 在android3.2上传遇到的问题

    1.报错信息如下: Gradle DSL method not found: 'google()'Possible causes: The project 'JustTest' may be usin ...

  2. 【转】Appium 中截取 element 图片作为对比,判断对比结果

    其实在https://github.com/gb112211/Adb-For-Test 里面有一个截取element进行对比的方法,但是在使用appium时是无法使用的,因为其用到了uiautomat ...

  3. 使用html2canvas将html标签转化为图片

    有些时候我们无法用常规的截图工具截取网页内容,这时可以尝试以下方法: 1.首先允许跨域,Chrome浏览器可以在快捷方式->属性->目标栏里添加: --disable-web-securi ...

  4. spring boot 使用及最佳实践

    第一部分,spring boot 文档 Spring boot的使用 使用maven进行构建 用户可以通过继承spring-boot-starter-parent来获取默认的依赖. l  默认java ...

  5. 关于java调用Dll文件的异常 %1 不是有效的 Win32 应用程序。

    这个问题 将java的和编辑器都换成32位

  6. 6. 使用antd pro构建web页面

    前言 在开始之前,希望我们已经掌握了一部分react的知识,由于没有太多经验,其实我也是属于摸索阶段.这里假定我们已经了解了react,redux和dva/umi相关的知识.并有做过相关练习. 如果还 ...

  7. python破解网吧收费系统,远控网吧电脑设备!

      我今天呢 , 我就没事跟着朋友喝酒喝酒啊.喝了很多啊.晚上到旁边的酒店开了一个房间,到了酒店才十点! 感觉没啥事情干的,那就去网吧走走看把,看到是一个嘟嘟牛的,和上次是一样的.还是照常用MS170 ...

  8. 2019第十届蓝桥杯 E题 迷宫

    /*输入 30 50 01010101001011001001010110010110100100001000101010 00001000100000101010010000100000001001 ...

  9. java程序设计课程实验报告3

    北京电子科技学院(BESTI) 实     验    报     告 课程:java程序设计    班级:1353  姓名:陈都  学号:20135328 成绩:             指导教师:娄 ...

  10. 用java进行简单的万年历编写

    import java.util.Scanner; public class PrintCalendarDemo1 { public static void main(String[] args) { ...