BZOJ 3230 相似子串

题面

题解

首先我们要知道询问的两个子串的位置。

先正常跑一遍后缀数组并求出height数组。

对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过的,也就是考虑左端点在i、右端点在什么范围内时这个子串没有出现过——答案是右端点在[i + height[i] - 1, n]范围内时这个子串没出现过,即右端点在没有被“i与排在前一个的后缀的公共前缀”覆盖的部分时,这个子串没有出现过。

那么我们记录以每个i开头的新子串的数量,求前缀和,然后询问的时候二分就知道询问的字符串的开头、结尾是谁了。

用已有的height结合st表可以求出两个字符串的最长公共前缀,把字符串倒过来再跑一遍后缀数组,就能求出最长公共后缀了。

注意:

  1. 注意两个询问字符串开头/结尾相同的情况。
  2. 注意第二个后缀数组是倒过来的。hack数据:2 1 aabc 1 2
  3. 注意子串个数爆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表的更多相关文章

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

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

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

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

  3. [BZOJ4310] 跳蚤 - 后缀数组,二分,ST表

    [BZOJ4310] 跳蚤 Description 首先,他会把串分成不超过 \(k\) 个子串,然后对于每个子串 \(S\) ,他会从 \(S\) 的所有子串中选择字典序最大的那一个,并在选出来的 ...

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

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

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

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

  6. BZOJ 3230: 相似子串(后缀数组)

    传送门 解题思路 其实题目挺好想的.首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名.这样就可以预处理出每个后缀的\(l,r ...

  7. BZOJ 4278: [ONTAK2015]Tasowanie (后缀数组 / 二分+hash)

    直接归并,然后如果哪边的后缀字典序比较小就去哪边,然后就可以后缀数组 博客传送门- 但是本蒟蒻不会后缀数组 Upd:Upd:Upd:现在会了233.一道差不多的题:BZOJ 1692: [Usaco2 ...

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

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

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

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

随机推荐

  1. golang安装开发环境配置

    本机系统:fedora28 step 1 百度搜索 golang 到 go 语言中文网,下载 golang 包,如果是 linux 系统可以直接点击此连接,也可去 go 语言中文网, https:// ...

  2. windows下在idea用maven导入spark2.3.1源码并编译并运行示例

    一.前提 1.配置好maven:intellij idea maven配置及maven项目创建 2.下载好spark源码: 二.导入源码: 1.将下载的源码包spark-2.3.1.tgz解压(E:\ ...

  3. php从入门到放弃系列-03.php函数和面向对象

    php从入门到放弃系列-03.php函数和面向对象 一.函数 php真正的威力源自它的函数,内置了1000个函数,可以参考PHP 参考手册. 自定义函数: function functionName( ...

  4. Kubernetes探索学习002--Kubernetes的基本使用

    Kubernetes 的基本使用方法 原则:使用YAML文件描述你要部署的API对象! 以部署nginx静态站点为例,具体操作及内容如下 1.编写YAML文件 [root@kubernetes01 ~ ...

  5. 团队冲刺——Five

    昨天: 司宇航:web项目如何部署到公网,把网址做成桌面图标链接,登录记住密码功能. 王金萱:注册和登录界面,用户数据库的信息录入. 马佳慧:做界面. 季方:处理爬虫数据,实现统计功能. 遇到的问题: ...

  6. Task 6.4 冲刺Two之站立会议6

    今天对视频的画面质量进行了优化,又把所有的界面更换了一些比较美观的图片和背景.使界面看起来更加地合理,易于接受.

  7. Task 6.2站立会议二

    今天,我们开始自己开始编译运行代码了,服务器端,聊天界面的相关代码经过测试.大家都把自己的实验过程公布了,大家的交流对实验的进度也起到了很大的作用.明天我们要继续修改实验中的错误,然后大家一起把实验基 ...

  8. bata2

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  9. BloomFilter——大规模数据处理利器(爬虫判重)

    http://www.cnblogs.com/heaad/archive/2011/01/02/1924195.html Bloom Filter是由Bloom在1970年提出的一种多哈希函数映射的快 ...

  10. Good Time 冲刺 五

    一.今日完成任务情况及遇到的问题 第五天 日期:2018.6.18 王怡镔:今天继续在学习中完善编写页面,对之前的页面进行部分改进. 于鑫宇:修改完善布局,复习. 胡雅馨:今天继续改进页面,努力解决时 ...