BZOJ 3230 相似子串 | 后缀数组 二分 ST表
BZOJ 3230 相似子串
题面

题解
首先我们要知道询问的两个子串的位置。
先正常跑一遍后缀数组并求出height数组。
对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过的,也就是考虑左端点在i、右端点在什么范围内时这个子串没有出现过——答案是右端点在[i + height[i] - 1, n]范围内时这个子串没出现过,即右端点在没有被“i与排在前一个的后缀的公共前缀”覆盖的部分时,这个子串没有出现过。
那么我们记录以每个i开头的新子串的数量,求前缀和,然后询问的时候二分就知道询问的字符串的开头、结尾是谁了。
用已有的height结合st表可以求出两个字符串的最长公共前缀,把字符串倒过来再跑一遍后缀数组,就能求出最长公共后缀了。
注意:
- 注意两个询问字符串开头/结尾相同的情况。
- 注意第二个后缀数组是倒过来的。hack数据:2 1 aabc 1 2
- 注意子串个数爆int,需要开long long。
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c > '9' || c < '0')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 200005, INF = 0x3f3f3f3f;
char s[N];
int n, Q, buf1[N], buf2[N], sa[2][N], buc[N], rnk[2][N], height[2][N];
ll lg[N], st[2][N][20];
ll sum[N];
void suffix_sort(int t){
int *x = buf1, *y = buf2, m = 127;
for(int i = 0; i <= m; i++) buc[i] = 0;
for(int i = 1; i <= n; i++) buc[x[i] = s[i]]++;
for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
for(int i = n; i; i--) sa[t][buc[x[i]]--] = i;
for(int k = 1, p = 0; k <= n; k <<= 1, m = p, p = 0){
for(int i = n - k + 1; i <= n; i++) y[++p] = i;
for(int i = 1; i <= n; i++) if(sa[t][i] > k) y[++p] = sa[t][i] - k;
for(int i = 0; i <= m; i++) buc[i] = 0;
for(int i = 1; i <= n; i++) buc[x[y[i]]]++;
for(int i = 1; i <= m; i++) buc[i] += buc[i - 1];
for(int i = n; i; i--) sa[t][buc[x[y[i]]]--] = y[i];
swap(x, y), x[sa[t][1]] = p = 1;
for(int i = 2; i <= n; i++)
if(y[sa[t][i]] == y[sa[t][i - 1]] && y[sa[t][i] + k] == y[sa[t][i - 1] + k]) x[sa[t][i]] = p;
else x[sa[t][i]] = ++p;
if(p >= n) break;
}
for(int i = 1; i <= n; i++) rnk[t][sa[t][i]] = i;
for(int i = 1, k = 0; i <= n; i++){
if(rnk[t][i] == 1) continue;
if(k) k--;
int j = sa[t][rnk[t][i] - 1];
while(i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
height[t][rnk[t][i]] = k;
}
}
void init(){
for(int i = 1, j = 0; i <= n; i++)
lg[i] = i == (1 << (j + 1)) ? ++j : j;
}
void st_init(int k){
for(int i = 1; i <= n; i++) st[k][i][0] = height[k][i];
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[k][i][j] = min(st[k][i][j - 1], st[k][i + (1 << (j - 1))][j - 1]);
}
ll getmin(int k, int l, int r){
if(l == r) return INF;
if(l > r) swap(l, r);
int j = lg[r - l];
return min(st[k][l + 1][j], st[k][r - (1 << j) + 1][j]);
}
int main(){
read(n), read(Q);
scanf("%s", s + 1);
init();
suffix_sort(0);
st_init(0);
for(int i = 1, j = n; i < j; i++, j--) swap(s[i], s[j]);
suffix_sort(1);
st_init(1);
for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + n - sa[0][i] + 1 - height[0][i];
while(Q--){
ll a, b, al, bl, ar, br;
read(a), read(b);
if(a > sum[n] || b > sum[n]){
puts("-1");
continue;
}
al = lower_bound(sum + 1, sum + n + 1, a) - sum;
bl = lower_bound(sum + 1, sum + n + 1, b) - sum;
ar = rnk[1][n - (sa[0][al] + height[0][al] - 1 + a - sum[al - 1]) + 1];
br = rnk[1][n - (sa[0][bl] + height[0][bl] - 1 + b - sum[bl - 1]) + 1];
ll len = min(n - sa[1][ar] + 1 - sa[0][al] + 1, n - sa[1][br] + 1 - sa[0][bl] + 1);
ll x = min(len, getmin(0, al, bl));
ll y = min(len, getmin(1, ar, br));
printf("%lld\n", x * x + y * y);
}
return 0;
}
BZOJ 3230 相似子串 | 后缀数组 二分 ST表的更多相关文章
- bzoj 3230 相似子串 —— 后缀数组+二分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...
- BZOJ3230 相似子串[后缀数组+二分+st表]
BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...
- [BZOJ4310] 跳蚤 - 后缀数组,二分,ST表
[BZOJ4310] 跳蚤 Description 首先,他会把串分成不超过 \(k\) 个子串,然后对于每个子串 \(S\) ,他会从 \(S\) 的所有子串中选择字典序最大的那一个,并在选出来的 ...
- bzoj 3230 相似子串——后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好 ...
- BZOJ 3230 相似子串 ——后缀数组
题目的Source好有趣. 我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数. 然后二分求出当前的字符串. 然后就是正反两次后缀数组求LCP的裸题了. 要注意,这时两个串的起点可能会相同 ...
- BZOJ 3230: 相似子串(后缀数组)
传送门 解题思路 其实题目挺好想的.首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名.这样就可以预处理出每个后缀的\(l,r ...
- BZOJ 4278: [ONTAK2015]Tasowanie (后缀数组 / 二分+hash)
直接归并,然后如果哪边的后缀字典序比较小就去哪边,然后就可以后缀数组 博客传送门- 但是本蒟蒻不会后缀数组 Upd:Upd:Upd:现在会了233.一道差不多的题:BZOJ 1692: [Usaco2 ...
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...
- 【BZOJ3230】相似子串 后缀数组+二分+RMQ
[BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...
随机推荐
- int str input的运用
- c语言数字图像处理(四):灰度变换
灰度变换 灰度变换函数 s = T(r) 其中r为输入图像在(x, y)点处的灰度值,s为输出图像在(x, y)点处的灰度值 灰度变换的作用 上图所示的两幅T(s)函数的图像曲线,第一幅图可以增强 ...
- SQL知识点脑图(一张图总结SQL)
sql语言的分类DDL:create drop alter DML:insert delete update DCL:rollback grant revoke commit 概要,主外键,视图,索引 ...
- 服务发现与消费 --> Spring Cloud Eureka
在上两篇文章中,我们已经搭建起微服务架构中的核心组件 服务注册 中心(包括单节点模式和高可用模式).同时, 还对上一章中实现的Spring Boot入门程序 做了改造. 通过简单的配置,使该程序注册到 ...
- phpcms 容许英文目录有空格
在PHPCMS添加栏目里面,有个选项是 英文目录,这里目录可以用作伪静态功能.这么英文不能有空格等特殊字符.但是如果页面中需要引用包含空格的字符呢,例如,关于我们页面,我要显示英文about us.那 ...
- 使用yum安装文件时提示安装文件重复问题2:nodejs-10.15.3-1nodesource.x86_64: [Errno 256] No more mirrors to try.
原因:yum命令缓存问题 解决办法: sudo yum clean all
- 【Docker】第一篇 Docker的初始化安装部署
一.Docker基础 Dacker倡导的理念:一个容器一个进程 Docker的版本了解: Docker从1.13版本之后采用时间线的方式作为版本号,分为社区版CE和企业版EE. 社区版是免费提供给个人 ...
- cobbler部署以及使用
常用软件安装及使用目录 资源链接:https://pan.baidu.com/s/1yfVnuSgY5vOTh-B74tpVyw 网盘分享的文件在此 cobbler第一次操作history. ec ...
- delphi 图像处理 图像左旋右旋
procedure TDR_QM_ZP_Form.btn_ZXClick(Sender: TObject); //图像左旋 begin screen.Cursor := crhourglass; my ...
- validating & update ctabfolder css
总是查错 结果把validating全部都反选,然后老是update ctabfolder css update ctabfolder css has encountered a problem An ...