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. Unity中使用C#实现UDP广播

    没有系统的学习过网络,想做联机游戏还真是费劲,想做在局域网内实现自动搜索服务器的功能,然后就想到了使用UDP进行广播,把服务器的信息广播给每一个玩家. Socket udpSocket = new S ...

  2. CF 675E Trains and Statistic

    草稿和一些题解而已 因为指针太恶心了 所以query决定还是要试试自己yy一下 #include<cstdio> #include<cstring> #include<i ...

  3. python - 定时清理ES 索引

    只保留三天 #!/usr/bin/env python3 # -*- coding:utf-8 -*- import os import datetime # 时间转化为字符串 now_time = ...

  4. 使用spring整合Quartz实现—定时器

    使用spring整合Quartz实现—定时器(Maven项目做演示) 不基于特定的基类的方法 一,开发环境以及依赖的jar包 Spring 4.2.6.RELEASE Maven 3.3.9 Jdk ...

  5. 对于新手来说,Python 中有哪些难以理解的概念?

    老手都是从新手一路过来的,提起Python中难以理解的概念,可能很多人对于Python变量赋值的机制有些疑惑,不过对于习惯于求根究底的程序员,只有深入理解了某个事物本质,掌握了它的客观规律,才能得心应 ...

  6. 用python实现数字图片识别神经网络--启动网络的自我训练流程,展示网络数字图片识别效果

    上一节,我们完成了网络训练代码的实现,还有一些问题需要做进一步的确认.网络的最终目标是,输入一张手写数字图片后,网络输出该图片对应的数字.由于网络需要从0到9一共十个数字中挑选出一个,于是我们的网络最 ...

  7. spring study

    Dependency Injection The Inversion of Control(IoC) is a general concept, and it can be expressed in ...

  8. LeetCode 303. Range Sum Query - Immutable (C++)

    题目: Given an integer array nums, find the sum of the elements between indices iand j (i ≤ j), inclus ...

  9. Scrum Meeting 10.27

    1.会议内容: 姓名 今日任务 明日任务 预估时间(h) 徐越 配置SQLserver 学习本地和服务器之间的通信 4 卞忠昊 找上届代码的bug 学习安卓布局(layout)的有关知识,研究上届学长 ...

  10. 结对项目:SudokuGame

    1. Github项目地址:https://github.com/ZiJiaW/SudokuGame GUI在BIN目录下的SudokuGUI.rar中,解压后打开SudokuGame.exe即可.2 ...