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 输出 ...
随机推荐
- First Day!
刚申请博客第一天,多多关照! 小弟,给各位大佬递茶! 出现什么错误, 还请明确指出! 现在, 正在找工作, 如果有老哥, 公司缺人, 何不让老弟我去试试! 不入前端, 不知水深. 一入前端, 如入泥潭 ...
- 高可用注册中心 ->Spring Cloud Eureka
在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况, 所以在生产 环境中必须对各个组件进行高可用部署, 对于微服务如此, 对于服务注册中心也一样. 但 是到本节为止,我们一直都在使用单节点 ...
- python的eval和json.loads(),json.dumps()
eval() 将字符串当成一个表达式去执行,可以想象成一个去字符串然后执行的操作. In [1]: s = '3*8' In [2]: eval(s) Out[2]: 24 eval()和json.l ...
- Selenium笔记:PO模型
所有用到的页面都定义成一个类,继承自基础的Page类 把页面中用到的元素定义成方法 把页面上一些操作定义成方法
- k倍区间:前缀和
[蓝桥杯][2017年第八届真题]k倍区间 题目描述 给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数 ...
- 《The Mythical Man-Month(人月神话)》读后感(1)
临近考试周,这里我通过平时阅读的<人月神话>十九个章节和知乎.简书等网页中网友们对<人月神话>的读后感,对书中各个章节进行简单的总结,以下均为个人手打观点的思考与整合,仅供大家 ...
- iOS开发日常遇到问题记录
1. [self.navigationController.navigationBar setTranslucent:NO]; iOS 7 之后,setTranslucent=yes 默认的 则状 ...
- 2017秋软工 —— 本周PSP
1. PSP 2. PSP饼图 3. 累计进度条 4. 累计折线图
- TeamWork#3,Week5,Scrum Meeting 11.13
最近我们根据之前发现的问题, 补充了相关知识,正在努力修复出现的问题,调整程序结构. 成员 已完成 待完成 彭林江 之前没有考虑到网站信息更新导致的程序可变性,正在调整爬虫程序结构 更换爬虫结构 郝倩 ...
- spring冲刺计划
会议召开时间表 日期 时间 内容 05/09 21:00-22:00 讨论题目(未果) 05/10 21:00-21:30 确定题目(网络助手) 05/13 21:00-21:45 讨论软件页面设计 ...