您的位置:首页 > 其它

CF319D Have You Ever Heard About The World? 二分Hash判断可行解

2016-04-13 16:31 597 查看

题目大意

给你一个字符串SS,要求你每次找到一个最短的形如XXXX(即由两个相同的字符串拼接而成)的子串,如有多个找最左边的那一个。然后把这个字符串从XXXX变成XX,问无法操作后的字符串是什么?

|S|≤50000|S| \leq 50000

解题思路

要解决这题,有两个关键的性质是一定要发现的。

1. 每次找到的符合要求的子串的程度是不减的。

2. 在删除相同长度的子串时一定是从左到有又删除的。

第二个性质是显然的,题目就是这样规定的。那么第一个性质是为什么呢?我们可以发现,当进行一次操作后(从XXXX变成XX)那么对于这个子串前后长度小于等于|X||X|的子串是没有影响的,所以第二条性质是成立的。

知道这两个性质后,我们如何判断是否存在一个长度为2L2L的XXXX呢?我们可以每隔LL设立一个观察点,那么这个长度为2L2L的子串一定会跨越两个相邻观察点。我们对于两个相邻的观察点求一个最长公共前缀和最长公共后缀,这个可以用二分加HashHash判断解决。如果这两个长度加起来大于LL,那么这就是一个可以进行操作的子串。这样做的之间复杂度是O(|S|LlogL)O(\frac{|S|}{L}logL)或O(|S|L)O(\frac{|S|}{L}),这样似乎一样会超时。但是我们发现,如果我们把长度相同的子串放在一起处理,我们发现不同长度的子串最多只会有O(|S|−−−√)O(\sqrt{|S|})种。那么每次做之前先判断一下有没有可行解,那么复杂度就变成了O(|S|1.5+|S|log2|S|)O(|S|^{1.5} + |S|log^2|S|)。

程序

//CF319D Have You Ever Heard About The World? YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
typedef unsigned long long ULL;

const int MAXS = 29, MAXN = 50005;

ULL Sum[MAXN], Fac[MAXN];
int Len, Flag[MAXN];
char S[MAXN];

ULL GetHash(int l, int r) {
return Sum[r] - Sum[l - 1] * Fac[r - l + 1];
}

int GetLeft(int x, int y) {
int Ans = 0, l = 1, r = Len - max(x, y) + 1;
while (l <= r) {
int Mid = (l + r) >> 1;
if (GetHash(x, x + Mid - 1) == GetHash(y, y + Mid - 1)) Ans = Mid, l = Mid + 1; else
r = Mid - 1;
}
return Ans;
}

int GetRight(int x, int y) {
int Ans = 0, l = 1, r = min(x, y);
while (l <= r) {
int Mid = (l + r) >> 1;
if (GetHash(x - Mid + 1, x) == GetHash(y - Mid + 1, y)) Ans = Mid, l = Mid + 1; else
r = Mid - 1;
}
return Ans;
}

bool Check(int len) {
for (int i = 1; i + len - 1 <= Len; i += len) {
if (S[i] != S[i + len]) continue;
if (GetLeft(i, i + len) + GetRight(i, i + len) - 1 >= len) return 1;
}
return 0;
}

int main() {
scanf("%s", S + 1);
Len = strlen(S + 1);
Fac[0] = 1;
for (int i = 1; i <= Len; i ++) {
Fac[i] = Fac[i - 1] * MAXS;
Sum[i] = Sum[i - 1] * MAXS + S[i];
}
for (int len = 1; len <= Len / 2; len ++) {
if (!Check(len)) continue;
for (int i = 1; i <= Len + 1 - 2 * len; i ++)
if (Flag[i] != len && GetHash(i, i + len - 1) == GetHash(i + len, i + 2 * len - 1))
for (int j = i; j <= i + len - 1; j ++) Flag[j] = len;
int New = 0;
for (int i = 1; i <= Len; i ++) if (Flag[i] != len) S[++ New] = S[i];
Len = New;
for (int i = 1; i <= Len; i ++) Sum[i] = Sum[i - 1] * MAXS + S[i];
}
for (int i = 1; i <= Len; i ++) printf("%c", S[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: