【ybt金牌导航2-2-2】不可重叠串
不可重叠串
题目链接:ybt金牌导航2-2-2
题目大意
给你一个数组,要你找两个长度相等,互不相交的两个连续部分,使得一个部分可以通过所有数加或减一个数得到的第二个部分。
思路
首先我们看到是把所有数加或减一个值得到另外一个,那我们其实就是那两个部分的波动是一样的。
那你可以求出这个数比前面那个数大或小了多少。然后如果一直都是对上的,那这两个部分就可以选,如果对上的数的长度是 n n n,那这两个部分的长度就是 n + 1 n+1 n+1。(因为每两个数之间才会产生相差的数一个)
那你把每个数组看成一种字符,它就相当于在一堆字符串里面找两个最长的不相交的一样的串。
那你其实可以这样做,求 SA 后缀数组,然后把关键的 height 数组求出。
那你其实就相当于找两个地方作为起点,然后用 height 求出这两个为起点的最长公共前缀,然后判断相交,在不相交的之间取长度最大值。
但是很明显它超时了。
那你看到求的是最长公共前缀,那我们在想到求最长公共前缀就是枚举排名然后取
min
\min
min,我们可以考虑二分能的长度。
那我们就枚举排名,会把它分成一些区间,每个区间里面取
min
\min
min 值一定会大于等于你二分的值。(而且再往外扩张就不满足)
那也就是说,对于某个区间的里面的每个位置,从它们之间选任意两个位置作为开始,最长公共前缀一定是大于等于你二分要判断的值,那你就只要看是否有不会相交的就可以。
至于如何判断相交,你就找这个区间里面位置最前和最后的(因为这两个最不容易相交),至于怎么判断相交,最前面的是
m
i
n
n
minn
minn,最后面的是
m
a
x
n
maxn
maxn,你二分的长度是
m
i
d
mid
mid,那就是要
m
a
x
n
−
m
i
n
n
>
=
m
i
d
+
1
maxn - minn >= mid + 1
maxn−minn>=mid+1。
(因为你一个数是由原来的两个数相减得来,所以你
m
i
d
mid
mid 要加一,就像输出答案的一样)
然后最后输出的时候记得判断长度是否大于 5 5 5,就没什么了。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n, now, lst, r[20001], m;
int box[20001], sa[20001], rank[20001];
int fir[20001], kind, ynum, xx[20001], yy[20001];
int height[20001];
void sort_(int m, int *x, int *y) {//基数排序
for (int i = 1; i <= m; i++)
box[i] = 0;
for (int i = 1; i <= n; i++)
box[x[i]]++;
for (int i = 2; i <= m; i++)
box[i] += box[i - 1];
for (int i = n; i >= 1; i--)
sa[box[fir[i]]--] = y[i];
}
void SA(int *r, int *sa, int n, int m) {//SA求后缀排名
int *x = xx, *y = yy;
for (int i = 1; i <= n; i++)
x[i] = fir[i] = r[i];
for (int i = 1; i <= n; i++)
y[i] = i;
for (int i = 1; i <= n; i++)
fir[i] = x[y[i]];
sort_(m, x, y);
for (int j = 1; ; j <<= 1) {
m = kind;
ynum = 0;
for (int i = n - j + 1; i <= n; i++)
y[++ynum] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > j) y[++ynum] = sa[i] - j;
for (int i = 1; i <= n; i++)
fir[i] = x[y[i]];
sort_(m, x, y);
swap(x, y);
kind = 1;
x[sa[1]] = 1;
for (int i = 2; i <= n; i++) {
if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j])
x[sa[i]] = kind;
else x[sa[i]] = ++kind;
}
if (kind == n) break;
}
}
void get_height(int *r, int *sa, int n) {//求出SA的height
int k = 0, j;
for (int i = 1; i <= n; i++) rank[sa[i]] = i;
for (int i = 1; i <= n; i++) {
if (k) k--;
j = sa[rank[i] - 1];
while (r[i + k] == r[j + k]) {
k++;
}
height[rank[i]] = k;
}
}
bool ck(int mid) {//判断能不能找到这么长的长度
int maxn = sa[1], minn = sa[1];
for (int i = 2; i <= n; i++) {
if (height[i] < mid) {//断开
maxn = sa[i];
minn = sa[i];
}
else {
maxn = max(maxn, sa[i]);
minn = min(minn, sa[i]);
if (maxn - minn >= mid + 1) return 1;//看是否不相交
}
}
return 0;
}
void csh() {
lst = 0;
}
int main() {
scanf("%d", &n);
// while (n) {
// csh();
for (int i = 1; i <= n; i++) {
scanf("%d", &now);
r[i] = now - lst + 100;
lst = now;
}
SA(r, sa, n, 200);
get_height(r, sa, n);
int lef = 1, rig = n / 2, mid, ans = -1;//二分
while (lef <= rig) {
mid = (lef + rig) >> 1;
if (ck(mid)) {
ans = mid;
lef = mid + 1;
}
else rig = mid - 1;
}
if (ans == -1 || ans + 1 < 5) printf("0\n");
else printf("%d\n", ans + 1);//记得加一
// scanf("%d", &n);
// }
return 0;
}
相关文章
- 从150kHz 到 150MHz漫谈智能车竞赛中的无线导航技术
- 44jqGrid 实时数据处理-导航
- AndroidStudio制作底部导航栏以及用Fragment实现切换功能
- 《jQuery、jQuery UI及jQuery Mobile技巧与示例》——第9章 使用jQuery Mobile来导航页面 9.1技巧:搭建jQuery Mobile基础页面
- Qt编写自定义控件9-导航按钮控件
- 微信小程序自定义搜索导航栏
- 大数据导航网站
- 微信小程序学习第5天——页面导航(声明式与编程式导航)与页面事件(上拉与下拉刷新)
- iOS - 导航栏设置半透明或取消半透明
- IOS开发之Bug--iOS7View被导航栏遮挡问题的解决