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. appium+python+unittest 测试用例的几种加载执行方式

    利用python进行测试时,测试用例的加载方式有2种: 一种是通过unittest.main()来启动所需测试的测试模块:  一种是添加到testsuite集合中再加载所有的被测试对象,而testsu ...

  2. eclipse以MapReduce本地模式运行程序

    1.准备好所需的文件winutils.exe.hadoop-eclipse-plugin-2.7.3.jar.hadoop-common-2.2.0-bin-master.zip 下载路径:http: ...

  3. zabbix第一篇:zabbix安装及使用

    常用软件安装及使用目录 一:搭建zabbix命令集 cat /etc/redhat-release uname -r getenforce systemctl status firewalld.ser ...

  4. redis解决商品秒杀问题

    博主最近在项目中遇到了抢购问题!现在分享下.抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:1 高并发对数据库产生的压力2 竞争状态下如何解决库存的正确减少("超卖" ...

  5. 1、数据库与excel表格的数据导入导出

    1.居民用户界面中,excel数据导入导出: 2.其他5张表数据显示到本角色主页的container容器中.

  6. 软工实践-Beta 冲刺 (4/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...

  7. Internet History, Technology and Security (Week5.1)

    Week5 The Transport layer is built on the Internetwork layer and is what makes our network connectio ...

  8. rua出300道四则运算题

  9. php 把数字转化为大写中文

    PHP 数字转大写中文 PHP入门小菜鸟一枚.下午要求写一个把数字转成大写中文的脚本,百度了一波,几十个博客和网站都是用的那四个代码,第一个运行不了,第二个有问题,不合要求,第三个第四个太长,懒得看, ...

  10. Unity删除或更换天空盒

    Unity版本:5.6.2 点击菜单栏Window-->Lighting-->Settings 在弹出的窗口中,设置Skybox Material选项,从原来的默认天空盒更换成别的,或者选 ...