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]); }
相关文章推荐
- android设计模式之observer
- tcp/ip详解笔记001——概述
- LeetCode *** 11. Container With Most Water
- php数据库操作
- monodevelop引用dll及制作dll
- sonar学习及简单使用
- iOS9 - 状态栏的颜色各种问题
- 第六次实验报告(3)---电话薄程序
- Xcode使用介绍之一:Xcode简介+创建App应用
- new与delete
- Unity发布iOS上架总结
- 2012年"浪潮杯"山东省第三届ACM大学生程序设计竞赛:The Best Seat in ACM Contest
- js时间转换
- jvm内存管理
- chm 已取消到该网页的导航,打不开!
- mpp文件转换成jpg图片,可以用pdf文件做中转站
- iOS开发--_weak typeof(self) weakSelf = self
- MySQL 锁、事务隔离级别
- Android TextView部分文字指定颜色
- HTML5 参考手册(标签)