12年成都 E 贪心+KMP HDU 4468
2016-06-22 10:56
246 查看
12年成都 E 贪心+KMP HDU 4468
1. 为什么贪心的方法是这样,而不是把每一个不能匹配的字符单独加入明文呢?
对于字符串abcad,第四个a能与第一个a匹配,d不能与任何一个匹配。假设只是单独把d加入明文,则明文为abcd,然后就发现这样是不能构成字符串abcad的。证毕。
2. 为什么这样贪心是正确的?
没有很明确的证明方法,假设对于字符串A-B-C(表示子串而不是字母),那么如果A和B不匹配,肯定要把B整个加进去才能合法,不存在不加B就能合法的情况。换句话说,遇到不合法的子串,你可以选择多加,但是这个子串一定要加入明文,而且多加的部分设为B,必要部分为A,如果后面出现A不匹配的情况,多加的B也是没有任何作用的。
重要的事情说在前面,以后KMP照这样写。 赛中的时候过的人不多,也没有什么具体的思路。发现问题可以转化为最短后缀,使得前面的字符串都是它的子串。甚至想着能不能枚举后缀,O(1)或者log的查询。然后就走入死角了。 实际上还是字符串的题目做的不够多。如果是倒序遍历的话,那查询的字符串操作也应该是倒序的,不存在一个倒序一个正序之说。再者,要检测之前是否为其子串,最坏情况下需要把整个字符串都遍历一遍,也不存在可以log查询的方法。 正解是贪心+KMP。过程就是如何构造最小的明文s。存储一个明文s,在向后遍历的过程中,如果当前节点i不能与字符串匹配,则从上一次记录的last指针处,把密文中[last,i]都加入明文。最后输出答案时,由于明文一定为后缀,所以之前产生明文可能出现最后后缀匹配完全、但是明文没有遍历完全的情况,这时候要把整个后缀都加入明文,答案为(密文长度-last指针+明文长度)。 那么,问题来了。
1. 为什么贪心的方法是这样,而不是把每一个不能匹配的字符单独加入明文呢?
对于字符串abcad,第四个a能与第一个a匹配,d不能与任何一个匹配。假设只是单独把d加入明文,则明文为abcd,然后就发现这样是不能构成字符串abcad的。证毕。
2. 为什么这样贪心是正确的?
没有很明确的证明方法,假设对于字符串A-B-C(表示子串而不是字母),那么如果A和B不匹配,肯定要把B整个加进去才能合法,不存在不加B就能合法的情况。换句话说,遇到不合法的子串,你可以选择多加,但是这个子串一定要加入明文,而且多加的部分设为B,必要部分为A,如果后面出现A不匹配的情况,多加的B也是没有任何作用的。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <string> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 1e5 + 5; char s[MAXN], t[MAXN]; int f[MAXN], cnt; void add(int l, int r) { for(int i = l, j = f[cnt - 1] ; i <= r ; i++) { t[cnt++] = s[i]; while(j != -1 && t[j + 1] != s[i]) j = f[j]; if(t[j + 1] == s[i]) j++; f[cnt - 1] = j; } } int main() { int cas = 0; while(scanf("%s", s) != EOF) { int n = strlen(s); for(int i = 0 ; i < n ; i++) t[i] = '#'; cnt = 0; t[cnt++] = s[0], f[cnt - 1] = -1; int last = 1; for(int i = 0 , j = -1 ; i < n ; i++) { while(j != -1 && t[j + 1] != s[i]) j = f[j]; if(t[j + 1] == s[i]) j++; if(j == cnt - 1) { last = i + 1; } else if(j == -1){ add(last, i); last = i + 1; } // printf("i = %d, j = %d, last = %d, cnt = %d, s[i] = %c, s[j + 1] = %c\n", i, j, last, cnt, s[i], t[j + 1]); // printf("f "); // for(int k = 0 ; k < cnt ; k++) printf("%d ", f[k]); // printf("\n"); } // printf("last = %d\n", last); printf("Case %d: %d\n", ++cas, n + cnt - last); } return 0; }
相关文章推荐
- sqlsever 导入大数据sql文件
- MYSQL 缓存详解 [myownstars] 经典博客
- web开发规范文档
- 15款优秀移动APP产品原型设计工具
- 直角三角形 射影定理 欧几里德定理
- android开发常用框架
- ibatis项目搭建
- Shiro 处理ajax请求 拦截登录超时 解决!
- 如果逃离北上广深,我们可以去哪里?
- “#ifdef __cplusplus extern "C" { #endif”的定义
- 使用ant直接执行shell命令
- Akka(二) - Future
- [SQL] 获取 Microsoft SQL Server 2008 的数据表结构
- 网站性能并发测试工具
- JPA执行原生SQL返回指定对象
- OpenGL帧缓冲区
- 1008
- seasLog学习笔记
- js退后一步并刷新
- Android设计模式——单例模式。