BZOJ 3230: 相似子串(后缀数组)
解题思路
其实题目挺好想的。首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(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: 相似子串(后缀数组)的更多相关文章
- BZOJ 3230 相似子串 | 后缀数组 二分 ST表
BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...
- bzoj 3230 相似子串——后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好 ...
- BZOJ 3230 相似子串 ——后缀数组
题目的Source好有趣. 我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数. 然后二分求出当前的字符串. 然后就是正反两次后缀数组求LCP的裸题了. 要注意,这时两个串的起点可能会相同 ...
- bzoj 3230 相似子串 —— 后缀数组+二分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- poj 2774 最长公共子串 后缀数组
Long Long Message Time Limit: 4000MS Memory Limit: 131072K Total Submissions: 25752 Accepted: 10 ...
- URAL 1297 最长回文子串(后缀数组)
1297. Palindrome Time limit: 1.0 secondMemory limit: 64 MB The “U.S. Robots” HQ has just received a ...
- poj 1743 Musical Theme(最长重复子串 后缀数组)
poj 1743 Musical Theme(最长重复子串 后缀数组) 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复 ...
- BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )
二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...
随机推荐
- 怎么让小白理解intel处理器(CPU)的分类
https://www.zhihu.com/question/32669957 目录 如何选购台式机CPU? 1. 英特尔处理器简介(本文) 1.1 聊聊Intel Tick-Tock 2. AMD处 ...
- 探索Redis设计与实现12:浅析Redis主从复制
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- 远程桌面发生身份验证错误,要求的函数不受支持【WIN10家庭】或【专业版】--解决办法
10号更新后,远程出现如下报错 一.[家庭中文版]解决办法[亲自试验] 开启WIN家庭中文版的本地组策略 1.打开tet复制以下代码:@echo off pushd "%~dp0" ...
- CentOS 7.0 lamp
CentOS 7.0默认使用的是firewall作为防火墙,这里改为iptables防火墙. 1.关闭firewall: systemctl stop firewalld.service #停止fir ...
- Java学习之单例模式
单例设计模式:解决一个类在内存中只存在一个对象思想:1.为了避免过多创建类的对象,禁止此类以外创建对象(构造方法私有化)2.为了类可以被使用就必须创建此类对象,只好在本类中,创建对象3.在此类中创建的 ...
- python中输入多个数字(代码实现)
不多说,直接上代码: list1 = [] #定义一个空列表 str1 = input("请输入数值,用空格隔开:") # list2 = str1.split(" &q ...
- 洛谷 P2024 [NOI2001]食物链——带权值的并查集维护
先上一波题目 https://www.luogu.org/problem/P2024 通过这道题复习了一波并查集,学习了一波带权值操作 首先我们观察到 所有的环都是以A->B->C-> ...
- VIM 单词大小写转换
遇到大小写转换的时候,我觉得首先一个不应该直接放弃的选择就是采用正则表达式以及文本替换功能.不过,针对单个单词的转换在VIM中还有更为简单的方式. 组合命令gUw可以实现把光标当前所在位置的一个单词转 ...
- 如何在有scoped不影响elementUI 的其他页面组件,进行单页面修改的几种方法。
方式一:内联式css 内联式css , 优点:修改其他方便.缺点:造成页面臃肿,不利于后期维护. 方式二:外链css 外链css ,优点:对其他文件无影响,但会造成多个文件css (缺点) @imp ...
- Python之内建函数
常用内置函数: abs(): 绝对值 divmod(): 取商和模 pow(): 指数运算 round(): 返回浮点数 callable(): 判断函数是否可调用 isinstance(): ...