Portal -->bzoj3230

Description

  给你一个长度为\(n\)的字符串,把它的所有本质不同的子串按字典序大小排序,有\(m\)个询问,对于每一个询问\(x,y\)你需要回答排名\(x\)和子串和排名\(y\)的子串的最长公共前缀的平方和+最长公共后缀的平方和,如果询问不合法输出\(-1\)

  数据范围:\(n<=10^5,m<=10^5\),字符串只含有小写字母

Solution

​  虽然说貌似是。。后缀数组的裸题。。

​  但是场上没有写出来。。所以还是拎出来智力康复。。

  

  假设我们已经求出来\(sa\)数组和\(height\)数组,怎么来做这个题呢

​  首先需要判断询问合不合法,即要求出本质不同的子串的个数

​  这里可以直接用\(height\)数组求解,以样例为例子,放个图就比较好理解了:

​  简单一点来说就是非公共前缀的部分才能贡献新的本质不同的子串

​  所以我们稍微减一下就能够知道每一个后缀贡献了几个新的子串了,前缀和一下,把这个存前缀和的数组记为\(num\),则\(num[i]\)表示排名为\(1\)到\(i\)的后缀总共贡献了几个子串

  那么总的本质不同的子串的数量就是\(num[n]\)了

  

  接下来就是求最长公共前缀

​  如果知道了两个子串具体是啥当然很好做啦,只要子串的开头对应的两个后缀RMQ一下然后跟两个子串的长度取个\(min\)就好了

​  然而这里的主要问题出在我们要定位,但是因为我们有了\(num\)数组,所以直接二分一下就可以知道是由哪个后缀产生的了(找到第一个\(num\)大于等于的即可)

  最后是后缀

​  后缀的话其实就是反串的前缀,处理方式相同,但是定位的话我们可以直接根据求最长公共前缀的时候定位得到的信息,反转一下就能知道在反串中是由哪个后缀产生的了

  最后的话是。。输出的时候和子串数量的统计都需要long long

​   

​  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define TOP 20
using namespace std;
const int N=100010;
char s[N];
int n,m,lens;
struct Sa{/*{{{*/
int a[N],b[N],c[N];
int rk[N],height[N],sa[N],mn[N][TOP+1];
ll num[N];
int mx,n;
bool cmp(int x,int y,int len,int *r)
{return r[x]==r[y]&&r[x+len]==r[y+len];}
void sort(int n){
for (int i=0;i<=mx;++i) c[i]=0;
for (int i=1;i<=n;++i) ++c[a[b[i]]];
for (int i=1;i<=mx;++i) c[i]+=c[i-1];
for (int i=n;i>=1;--i) sa[c[a[b[i]]]--]=b[i];
}
void get_sa(int _n){
mx=0; n=_n;
for (int i=1;i<=n;++i) b[i]=i,a[i]=s[i]-'a'+1,mx=max(mx,a[i]);
int cnt=0;
sort(n);
for (int len=1;cnt<n;len*=2){
cnt=0;
for (int i=n-len+1;i<=n;++i) b[++cnt]=i;
for (int i=1;i<=n;++i)
if (sa[i]>len)
b[++cnt]=sa[i]-len;
sort(n);
swap(a,b);
cnt=1;
a[sa[1]]=1;
for (int i=2;i<=n;a[sa[i++]]=cnt)
if (!cmp(sa[i],sa[i-1],len,b))
++cnt;
mx=cnt;
}
}
void get_height(){
for (int i=1;i<=n;++i) rk[sa[i]]=i;
int k=0;
for (int i=1;i<=n;++i){
if (k>0) --k;
while (s[i+k]==s[sa[rk[i]-1]+k]) ++k;
height[rk[i]]=k;
}
cnt_num();
rmq();
}
void cnt_num(){
for (int i=1;i<=n;++i)
num[i]=num[i-1]+(n-sa[i]+1-height[i]);
}
void rmq(){
for (int i=1;i<=n;++i) mn[i][0]=height[i];
for (int j=1;j<=TOP;++j)
for (int i=n-(1<<j)+1;i>=1;--i)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]); }
int lcp(int x,int y){//x&y are ranks
if (x==y) return n-sa[x]+1;
if (x>y) swap(x,y);
++x;
int tmp=(int)(log(1.0*(y-x+1))/log(2.0));
return min(mn[x][tmp],mn[y-(1<<tmp)+1][tmp]);
}
int query(ll x,ll y,int &edx,int &edy,int &lenx,int &leny){
int posx,posy;
posx=lower_bound(num+1,num+1+n,x)-num;
posy=lower_bound(num+1,num+1+n,y)-num;
int ret=lcp(posx,posy);
lenx=n-sa[posx]+1-(num[posx]-x);
leny=n-sa[posy]+1-(num[posy]-y);
edx=n-sa[posx]+1-lenx+1;
edy=n-sa[posy]+1-leny+1;
return min(ret,min(lenx,leny));
}
int query2(int stx,int sty,int lenx,int leny){
int ret=lcp(rk[stx],rk[sty]);
return min(ret,min(lenx,leny));
}
}a[2];/*}}}*/ int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int tmp1,tmp2,edx,edy,lenx,leny;
ll x,y;
scanf("%d%d",&n,&m);
scanf("%s",s+1);
lens=strlen(s+1);
a[0].get_sa(lens); a[0].get_height();
for (int i=1;lens-i+1>i;++i) swap(s[i],s[lens-i+1]);
a[1].get_sa(lens); a[1].get_height();
for (int i=1;i<=m;++i){
scanf("%lld%lld",&x,&y);
if (x>a[0].num[n]||y>a[0].num[n]) printf("-1\n");
else{
tmp1=a[0].query(x,y,edx,edy,lenx,leny);
tmp2=a[1].query2(edx,edy,lenx,leny);
printf("%lld\n",1LL*tmp1*tmp1+1LL*tmp2*tmp2);
}
}
}

【bzoj3230】相似子串的更多相关文章

  1. BZOJ3230 相似子串[后缀数组+二分+st表]

    BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...

  2. BZOJ3230 相似子串 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9033092.html 题目传送门 - BZOJ3230 题意 给定字符串$s$.长度为$n$. 现在有$Q$组询 ...

  3. BZOJ3230: 相似子串

    3230: 相似子串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 913  Solved: 223[Submit][Status]Descripti ...

  4. BZOJ3230 相似子串 【后缀数组】

    题目分析: 容易想到sa排好序之后,子串排名就是前面的子串减去height数组.所以正着做一遍,倒着做一遍就行了. 代码: #include<bits/stdc++.h> using na ...

  5. BZOJ3230: 相似子串【后缀数组】

    Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数. 第2行是字符串S. 接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出共Q行,每行一个数表示每组 ...

  6. [BZOJ3230]相似子串(后缀数组)

    显然可以通过后缀数组快速找到询问的两个串分别是什么,然后正反各建一个后缀数组来求两个串的LCP和LCS即可. #include<cstdio> #include<cstring> ...

  7. 2018.11.30 bzoj3230: 相似子串(后缀数组)

    传送门 后缀数组入门题. 建立正反两个后缀数组算就行了. 代码: #include<bits/stdc++.h> #define ri register int using namespa ...

  8. bzoj3796(后缀数组)(SA四连)

    bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...

  9. 【BZOJ3230】相似子串 后缀数组+二分+RMQ

    [BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...

随机推荐

  1. 关于C语言中内存的3个问题

    1.程序为什么需要内存? 计算机程序 = 代码 + 结果,从宏观上理解,代码就是动作,而数据被动作加工,最终返回结果.程序是被放在内存中运行的,并且需要内存来存储一些临时变量,因此,对于程序来说,内存 ...

  2. selenium自动化之切换iframe

    许多人在执行脚本的时候会发现,明明自己的元素路径没写错啊!怎么还是报元素未找到的异常呢?是的,没错,你可能是遇上iframe啦!下面将介绍关于iframe的相关操作. 例子:以163邮箱登录页面为例 ...

  3. Appium+python 自动发送邮件(1)(转)

    (原文:https://www.cnblogs.com/fancy0158/p/10056091.html) SMTP:简单传输协议,实在Internet上传输Email的事实标准. Python的s ...

  4. Linux目录与文件操作

    文件命名规则: 1.严格区分大小写: 2.长度不能超过255个字符: 3.不能使用/当文件名 mkdir:创建空目录 -p:parent,父目录,逐级创建 -v:verbose,打印详细信息 命令行展 ...

  5. JMeter自学笔记1-环境安装

    一.写在前面的话: Jmeter是一款优秀的开源测试工具, 是每位测试工程师进阶过程中,需要熟悉并掌握的一款测试工具,熟练使用Jmeter能大大提高工作效率. Jmeter环境安装需要依赖JDK,所以 ...

  6. 获取Java线程返回值的几种方式

    在实际开发过程中,我们有时候会遇到主线程调用子线程,要等待子线程返回的结果来进行下一步动作的业务. 那么怎么获取子线程返回的值呢,我这里总结了三种方式: 主线程等待. Join方法等待. 实现Call ...

  7. redis 为什么快

    redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大. 不过,因为一般的内存操作都是简单存取操作,线程占用时间相对较短,主要问题在io上,因此,redis这种模型是 ...

  8. 【转】SWFUpload使用指南

    原文出自:http://www.runoob.com/w3cnote/swfupload-guide.html SWFUpload是一个flash和js相结合而成的文件上传插件,其功能非常强大.以前在 ...

  9. Python学习 - 入门篇2(更新中)

    前言 学习渠道:慕课网:Python进阶 记录原因:我只是想边上课边做笔记而已,呵呵哒 食用提示:教程环境基于Python 2.x,有些内容在Python 3.x中已经改变 函数式编程 定义:一种抽象 ...

  10. 201621123037 《Java程序设计》第13周学习总结

    作业13-网络 标签(空格分隔): Java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 思维导图: 其他: 网络编程:由客户端和服务器组成 - 服务器端 第一 ...