【BZOJ3230】相似子串 后缀数组+二分+RMQ
【BZOJ3230】相似子串
Description
Input
输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。
Output
输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。
Sample Input
ababa
3 5
5 9
8 10
Sample Output
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'组成
题解:一开始由于用SA还是SAM,然后看到下面的SOURCE一下子就不用犹豫了。。。这个提示也太明显了~
如何字典序第k小的子串呢?考虑二分,问题就变成了问一个子串的字典序,这个可以直接在height数组上求出,答案就是n-sa[i]-height[i]的前缀和。
然后相似程度就好求了,直接维护正串和反串的SA,用RMQ求出LCP即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
int n,m,Q;
char str[maxn];
int Log[maxn];
ll s[maxn];
inline ll rd()
{
ll ret=0,f=1; char gc=getchar();
while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();}
while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar();
return ret*f;
}
struct SA
{
int ra[maxn],rb[maxn],r[maxn],st[maxn],sa[maxn],rank[maxn],h[maxn],f[18][maxn];
void build()
{
int *x=ra,*y=rb,i,j,k,p;
for(i=0;i<n;i++) st[x[i]=r[i]]++;
for(i=1;i<m;i++) st[i]+=st[i-1];
for(i=n-1;i>=0;i--) sa[--st[x[i]]]=i;
for(j=p=1;p<n;j<<=1,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<m;i++) st[i]=0;
for(i=0;i<n;i++) st[x[y[i]]]++;
for(i=1;i<m;i++) st[i]+=st[i-1];
for(i=n-1;i>=0;i--) sa[--st[x[y[i]]]]=y[i];
for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p-1:p++;
}
for(i=1;i<n;i++) rank[sa[i]]=i;
for(i=k=0;i<n-1;h[rank[i++]]=k)
for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
for(i=1;i<n;i++) f[0][i]=h[i];
for(j=1;(1<<j)<n;j++) for(i=1;i+(1<<j)-1<n;i++) f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
}
inline int query(int a,int b)
{
if(a==b) return n-a;
a=rank[a],b=rank[b];
if(a>b) swap(a,b);
a++;
int k=Log[b-a+1];
return min(f[k][a],f[k][b-(1<<k)+1]);
}
}s1,s2;
int find(ll x)
{
int l=1,r=n+1,mid;
while(l<r)
{
mid=(l+r)>>1;
if(s[mid]>=x) r=mid;
else l=mid+1;
}
return r;
}
int main()
{
n=rd(),Q=rd();
scanf("%s",str);
int i,c,d,la,lb;
ll a,b;
for(i=0;i<n;i++) s1.r[i]=s2.r[n-i-1]=str[i]-'a'+1;
n++,m=27,s1.build(),m=27,s2.build(),n--;
for(i=2;i<=n;i++) Log[i]=Log[i>>1]+1;
for(i=1;i<=n;i++) s[i]=s[i-1]+n-s1.sa[i]-s1.h[i];
for(i=1;i<=Q;i++)
{
a=rd(),b=rd(),c=find(a),la=a-s[c-1]+s1.h[c],d=find(b),lb=b-s[d-1]+s1.h[d];
if(c==n+1||d==n+1) printf("-1\n");
else
{
a=min(min(la,lb),s1.query(s1.sa[c],s1.sa[d])),b=min(min(la,lb),s2.query(n-s1.sa[c]-la,n-s1.sa[d]-lb));
printf("%lld\n",a*a+b*b);
}
}
return 0;
}//5 3 ababa 3 5 5 9 8 10
【BZOJ3230】相似子串 后缀数组+二分+RMQ的更多相关文章
- BZOJ3230 相似子串[后缀数组+二分+st表]
BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...
- BZOJ 3230 相似子串 | 后缀数组 二分 ST表
BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...
- 【BZOJ3277/3473】串/字符串 后缀数组+二分+RMQ+双指针
[BZOJ3277]串 Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Inpu ...
- BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )
全部串起来做SA, 在按字典序排序的后缀中, 包含每个询问串必定是1段连续的区间, 对每个询问串s二分+RMQ求出包含s的区间. 然后就是求区间的不同的数的个数(经典问题), sort queries ...
- bzoj 3230 相似子串 —— 后缀数组+二分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...
- [BZOJ3230]相似子串(后缀数组)
显然可以通过后缀数组快速找到询问的两个串分别是什么,然后正反各建一个后缀数组来求两个串的LCP和LCS即可. #include<cstdio> #include<cstring> ...
- HDU 5558 Alice's Classified Message(后缀数组+二分+rmq(+线段树?))
题意 大概就是给你一个串,对于每个\(i\),在\([1,i-1]\)中找到一个\(j\),使得\(lcp(i,j)\)最长,若有多个最大\(j\)选最小,求\(j\)和这个\(lcp\)长度 思路 ...
- BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )
二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...
- 【bzoj4310】跳蚤 后缀数组+二分
题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究. 首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个 ...
随机推荐
- “/”和“\\”和feof();
filename=c:/test/abc.text filename=c:\\test\\abc.test "\\"为转义字符: feof();函数检测文件是否已经到达末尾(EOF ...
- 5分钟教程:如何通过UART获得root权限
写在前面的话 你知道物联网设备以及其他硬件制造商是如何调试和测试自家设备的吗?没错,绝大多数情况下,他们都会留下一个串行接口,这样就可以利用这个接口并通过shell来读取实时的调试日志或与硬件进行交互 ...
- Linux下自动监测并重启Apache服务脚本
为了达到一个高可用的基于Apache的网站环境,在Apache由于种种原因自动停止运行之后,想立即恢复网站访问,这就需要有个工具实时监测Apache的运行状态并能够自动重启httpd服务,写了一个简单 ...
- Snapdragon profiler连不上 android手机
adb devices也是空 开发者选项里面该开的都开了 就可以了 对了数据线不对也会连不上...
- 使用Ant构建简单项目
Apache Ant主要用于Java项目的构建.为什么要使用Ant: 1)项目构建不是简单的编译,各种资源需要合理构建,例如有的类需要压缩成jar文件,有的文件需要放在指定位置,有时候需要使用配置文件 ...
- HTML5 Canvas 六角光阑动态效果
光阑是光具组件中光学元件的边缘.框架或特别设置的带孔屏障,本人实现了结构比较简单的六角光阑,效果有点像宇航员在徐徐张开的飞船舷窗中看到逐渐完整的地球,下面四张图可以感受一下. 当然看动态效果才能真正体 ...
- IOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry) 转载
http://blog.csdn.net/he_jiabin/article/details/48677911 随着iPhone6/6+设备的上市,如何让手头上的APP适配多种机型多种屏幕尺寸变得尤为 ...
- web.py
#-*- coding:utf-8 -*- import web import json import requests #from web.contrib.template import rende ...
- 【Excle数据透视表】如何水平并排显示报表筛选区域的字段
原始效果 目标效果 解决方案 设置数据透视表"在报表区域筛选显示字段"为"水平并排" 步骤 方法① 单击数据透视表任意单元格→数据透视表工具→分析→选项→布局和 ...
- 读jQuery源码之整体框架分析
读一个开源框架,大家最想学到的就是设计的思想和实现的技巧.最近读jQuery源码,记下我对大师作品的理解和心得,跟大家分享,权当抛砖引玉. 先附上jQuery的代码结构. (function(){ / ...