传送门

解题思路

  其实题目挺好想的。首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名。这样就可以预处理出每个后缀的\(l,r\),查询的时候二分出来属于哪个后缀,用\(rmq\)求个\(lcp\)。倒过来处理的式子比较麻烦,要先将排名转化成位置,然后找到对应的倒过来的位置,最后在转化为排名,具体看代码。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath> using namespace std;
const int MAXN = 100005;
typedef long long LL; inline LL rd(){
LL x=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return f?x:-x;
} int n,q,m;
LL l[MAXN],r[MAXN]; struct SA{
int x[MAXN<<1],y[MAXN<<1],c[MAXN],sa[MAXN],rk[MAXN];
int num,height[MAXN],Min[MAXN][20];
char s[MAXN];
inline void get_SA(){m='z';
for(int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){num=0;
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=2;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
m=num;if(n==m) break;
}
}
inline void get_height(){
for(int i=1;i<=n;i++) rk[sa[i]]=i;int k=0,j;
for(int i=1;i<=n;i++){
if(rk[i]==1) continue;
if(k) k--;j=sa[rk[i]-1];
while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
}
inline void build(){
for(int i=1;i<=n;i++) Min[i][0]=height[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
}
inline int query(int x,int y){
if(x==y) return n+1;
if(x>y) swap(x,y);x++;int t=log2(y-x+1);
return min(Min[x][t],Min[y-(1<<t)+1][t]);
}
inline void prework(){
get_SA();get_height();build();
}
}A,B; inline int check(LL lim){
int L=1,R=n,mid;
while(L<=R){
mid=(L+R)>>1;
if(l[mid]<=lim && r[mid]>=lim) return mid;
if(l[mid]>lim) R=mid-1;
else L=mid+1;
}
} int main(){
int posA,posB,lenA,lenB,L,R;LL x,y;
n=rd(),q=rd();scanf("%s",A.s+1);
for(int i=1;i<=n;i++) B.s[n-i+1]=A.s[i];
A.prework();B.prework();
for(int i=1;i<=n;i++)
l[i]=r[i-1]+1,r[i]=l[i]+n-A.sa[i]-A.height[i];
// for(int i=1;i<=n;i++) cout<<l[i]<<" "<<r[i]<<endl;
while(q--){
x=rd(),y=rd();
if(x>r[n] || y>r[n]) {puts("-1");continue;}
posA=check(x);posB=check(y);
// cout<<posA<<" "<<posB<<endl;
lenA=A.height[posA]+x-l[posA]+1;
lenB=A.height[posB]+y-l[posB]+1;
// cout<<lenA<<" "<<lenB<<endl;
L=min(min(lenA,lenB),A.query(posA,posB));
R=min(min(lenA,lenB),B.query(B.rk[n-(A.sa[posA]+lenA-1)+1],B.rk[n-(A.sa[posB]+lenB-1)+1]));
// cout<<L<<" "<<R<<endl;
printf("%lld\n",(LL)L*L+(LL)R*R);
}
return 0;
}

BZOJ 3230: 相似子串(后缀数组)的更多相关文章

  1. BZOJ 3230 相似子串 | 后缀数组 二分 ST表

    BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...

  2. bzoj 3230 相似子串——后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好 ...

  3. BZOJ 3230 相似子串 ——后缀数组

    题目的Source好有趣. 我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数. 然后二分求出当前的字符串. 然后就是正反两次后缀数组求LCP的裸题了. 要注意,这时两个串的起点可能会相同 ...

  4. bzoj 3230 相似子串 —— 后缀数组+二分

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...

  5. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  6. poj 2774 最长公共子串 后缀数组

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 25752   Accepted: 10 ...

  7. URAL 1297 最长回文子串(后缀数组)

    1297. Palindrome Time limit: 1.0 secondMemory limit: 64 MB The “U.S. Robots” HQ has just received a ...

  8. poj 1743 Musical Theme(最长重复子串 后缀数组)

    poj 1743 Musical Theme(最长重复子串 后缀数组) 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复 ...

  9. BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

    二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...

随机推荐

  1. 【Vue】vue的双向绑定原理及实现

    vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,那么vue是如果进行数据劫持的,我们可以先来看一下通过控制台输出一个定义在vue初始化数据上的对象是个什么东西. 代码: var ...

  2. implements Serializable有什么作用

    转自 http://blog.csdn.net/dinghqalex/article/details/46009911

  3. CentOS 7 & php7.2安装 php-redis 扩展

    CentOS 7 & php7.2安装 php-redis 扩展 1.下载phpredis-developcd /tmpwget https://codeload.github.com/php ...

  4. 搭建Linux C语言开发环境

    1.操作系统 Windows操作系统:windows 7 and windows 10 2.开发工具和编译工具 开发工具:notpad++ 和 vim 编译工具:Cygwin64 Terminal 3 ...

  5. VMware 安装CenterOS

    虚拟机安装CenterOS 简介:在VMware里安装Linux虚拟机,这里使用的是(Center OS 6.9). 一.准备工作: 安装 VMware Workstation Pro,很简单不哔哔了 ...

  6. django 重写 mysql 连接库实现连接池

    django 重写 mysql 连接库实现连接池 问题 django 项目使用 gunicorn + gevent 部署,并设置 CONN_MAX_AGE 会导致 mysql 数据库连接数飙升,在高并 ...

  7. ubuntu16安装,配置前端开发环境

    1.安装ubuntu    使用usio制作U盘安装工具 2.安装搜狗输入法 3.安装QQ 4.安装nodejs node-v0.12.4 node-v0.12.4.tar.gz root@ubunt ...

  8. JVM运行时数据区及对象在内存中初始化的过程

    JVM运行时数据区 Java虚拟机所管理的内存区域,也称为运行时数据区,分为以下几个运行时数据区,如图所示 程序计数器:当前程序所执行字节码的行号指示器 程序计数器(Program Counter R ...

  9. Vue环境搭建及第一个helloWorld

    Vue环境搭建及第一个helloWorld 一.环境搭建 1.node.js环境安装配置  https://www.cnblogs.com/liuqiyun/p/8133904.html 或者 htt ...

  10. 通过rocketmq思考一下mq的设计取舍

    1. RocketMQ路由中心NameServer 2. RocketMQ消息发送 3. RocketMQ消息存储 4. RocketMQ消息消费 5. 消息过滤FiterServer 6. Rock ...