月球美容计划之那些天我们学过的KMP
2014-03-30 17:42
225 查看
问题描述
给两个字符串A,B,如A=“aaaaaaaaaaaaaaaaaaab”,B = “aaaaaaaab”,现在要求B是否是A的字串。
普通方法(O(nm)),对两个字符串进行双重枚举进行匹配知道找到A字符串中的B字符串。
for(i = 0;i < n;i++) { int tf = 0; for (k = 0;k < m;k++) { if (A[i] != B[k]) { tf = 0; break; } } if (tf) break; }
KMP小忆
KMP算法是通过分析模式字符串,预先计算每个位置发生不匹配的时候,所需跳转到的下一个比较位置,整理出来一个next数组,然后在上面的算法中使用。其达到的最终效果是,避免记录字符串A的指针i回溯,从而节省一个循环,也就是把O(mn),降为O(m)。
因此,也就可以说,KMP就是对O(nm)算法的改良,因此,其改良的地方,就是其关键。也就是说next就是KMP的精华所在。
犹抱琵琶的next函数
寒假时总结的模板void kmp (char *T,int next[]) { int j = 0,k = -1; next[0] = -1; while (T[j] != '\0') { if (k == -1 || T[j] == T[k]) { j++; k++; next[j] = k; }else k = next[k]; //相当于在求next的时候利用已求出的next } }
这个是非改进版的求next的函数。其next数组记录的数据x代表这个元素的位置,代表与整个字符串T的前x个字符相同。
不能小看next
next的使用不仅仅是其储存的内容的或用上,甚至不能小瞧在求next数组的时候其产的中间值。记得有一个题,就是让求字符串中循环节的长度,记得以前做类似的题目的时候是用的双重枚举,如果用KMP的话,也是可以省掉的一层循环。
做这题求next的代码如下:
int fnext (char *T,int next[]) { int j = 0,k = -1; next[0] = -1; while (T[j] != '\0') { if (k == -1 || T[j] == T[k]) { j++; k++; next[j] = k; } else k = next[k]; } if (k == 0) return -1; return j - k; }
其中j指针指的是主字符串(不回溯的指针),k字符串指的是辅字符串(可以回溯),当next数组构建完成的一瞬间,想象一下j和k的样子,现在j指向了主字符串的最末尾,而k不一定指向哪,但可以肯定的是,如果T[j] != T[k],k一定不等于0,否则,k指向的位置x,一定会让字符串后x位与前x位相等。
也就是说,如果存在循环节,其循环节的长度就是j - k。
KMP带来的反思
KMP使用一个next数组让一种O(nm)的算法变为了O(n),有点用空间换时间的味道,像这种用空间换时间还有什么可以举一反三的吗?KMP是利用空间,避免了在双重枚举的时候,一个指针的反复回溯,减少了一层循环。
那么,在做模拟题,尤其涉及到枚举的模拟题的时候,是不是在时间吃紧的情况下可以考虑通过存储某些东西,来去掉一层循环呢?
相关文章推荐
- 月球美容计划之最小生成树(MST)
- 月球美容计划之图的储存结构汇总
- 月球美容计划之最短路
- 月球美容计划之最短路
- 月球美容计划之并查集
- 月球美容计划之维尼的背包(基础篇)
- 月球美容计划之最短路
- 月球美容计划之最小生成树(MST)
- 月球美容计划之图的储存结构汇总
- 软件开发成本评估:我们知道和不知道的那些事
- 那些幸福我们都还拥有
- 我们拿这10年做什么——让计划服从变化
- 挖一挖C#中那些我们不常用的东西之系列(3)——StackTrace,Trim
- 马斯克的十年计划:随处可见充电桩 轻轻松松月球旅行 | 精选
- 那些年,我们一起用SkyDrive解的那些“急”
- 2012年终总结与2012年度计划那些事
- 与老板的对话,是我们的计划不合理,还是你太幻想了,如果这周项目能验收掉,我就把这篇文章吃掉。
- 硝烟中的Scrum和XP-我们如何实施Scrum 4)制定spring计划 (Part 2/2)
- 硝烟中的Scrum和XP-我们如何实施Scrum 12)发布计划 13)组合XP
- 聊聊那些我们不应该被百度惯坏的搜索技巧