吾之简单的KMP算法学习,字符串操作基本功
2015-12-30 20:39
204 查看
字符与文字
字符如此普遍,构成我们语言的文字在电脑以看来字符为单位是十分合理的,我们人类的任何语言都对文字赋予了丰富的内涵,处理好每一个字符是一个负责任的一流工程师应该做的。即便是简单到不能再简单的Lisp也是把一切归为几个字符,可见几个字符的重要性。字符串,一堆又一堆的字符,使之排序构成串,字符串操作无非有四:增、删、改、查 [ 切分 ]。
字符串操作在我看来分为两类:基于比较的和基于坐标的,比较结果,和坐标位置是我们操作字符串的基准(规则)。
搜索和遍历
创造字符串,第一步就是看看它长个啥样子,遍历整个字符串便是首要目标。程序就是在循环之中寻找目标,循环之多少决定程序之快慢,深度亦深亦浅,全在于工程师,有尺有度,有条不紊,故此Python书上。
字符串匹配算法(正文)
字符串匹配,一文一字要挨个来,有人喜从头起,有人喜欢尾始,因人而异,因时而异本王所讲皆是从左开始按正序开始匹配,而且只是简单讲述KMP算法和暴力搜索
暴力搜索
人的一生总是从简单直接粗暴的开始,以暴制暴方便入门。要粗暴,那就要直接裸写代码强行匹配,完全不怂!写完代码再来看以下代码的同学日后必成大器,日后必成大器,日后必成大器,重要的文字、文字、文字说三遍!
def brute_force(text,find): #暴力搜索 start = 0 # text 比较游标 sub = 0 # find 比较游标 restart = 0 while start!=len(text): if text[start]==find[sub]: start+=1 sub+=1 else: restart+=1 start=restart #text 游标从次一项开始 sub = 0 # find 游标从 头开始 if sub==len(find): return start - sub #由于start 游标停在最后的匹配字符上,所以我们要减去搜索串find的长度 return -1如果以上代码看不懂,我教你一个简单快速的方法看懂,鼠标右下角,立马就懂!
当你分析懂了暴力搜索的原理(我的代码怎么表达的,弄懂restart 是什么意思),你就可以很快搞懂KMP算法是个什么鬼!
简而言之,比较俩字符串,<小明不知道小敏在哪里,而晓敏知道小明知道晓敏知道小敏在哪里> 以及<晓敏>
我们一眼就可以知道 第一个分句里没有,第二个分句有<晓敏>,我们看一眼便知道整个句子,所以瞬间出答案,同样的道理,如若我们能亚索(不,压缩)字符串做到快速遍历也是搜索之良方,不过我们不采取这种方法,而是采取一个小策略我们把<晓敏>拆开,这个句子<敏>出现次数较多,如果没有<敏>这个字,我们就可以直接跳过一些距离,直接从<小明不知道小敏在哪里>中的<在>继续查找,我想大概的意思你应该懂了那么一丢丢!
在伊犁,
很明显,GB后A 和O明显不匹配,再一个字符一个字符比较<ADA>,你这样做似不似傻
,完全可以跳过其中的<ADA>跳到第二个<G>比较就可以提高效率
所以呢,我们需要机智的预先算出来<GBORGAB>中 两个G之间的距离来确定必要时跳跃几个字符!
如何计算每个字符需要的跳转位置呢!
假使<GBORGAB>是一个特别集合,使用其子集(如:G、GB、GBO等)前缀和后缀的共有元素来确定对应字符的跳转距离!共有元素组成的集合成为跳转表
代码如下:
def kmp(text,find): #经典的KMP(Knuth-Morris-Pratt)算法 match = [] prefix_list = [] suffix_list = [] #生成 "部分匹配表" for x in xrange(1,len(find)+1): # xrange(1,10) 生成1~9的序列 str_src = find[0:x] #str_src原字符串 prefix_list = prefix(str_src) suffix_list = suffix(str_src) intersection_list = [tmp for tmp in prefix_list if tmp in suffix_list] intersection_str = ''.join(intersection_list) match.append(len(intersection_str)) print match #比较操作 start = 0 # text 比较游标 sub = 0 # find 比较游标 restart = 0 #跳转游标 matched_char = 0 #当前已匹配字符数 while start!=len(text): #只要没有读取到text的最后一个字符就继续 读 print 'comparing '+str(start)+' and '+str(sub) if text[start]==find[sub]: print 'found '+str(start)+' and '+str(sub)+'is equal' matched_char += 1 start+=1 sub+=1 else: if matched_char == 0: restart +=1 else: restart+= (matched_char - match[sub-1]) matched_char = 0 start=restart #text 游标从次一项开始 sub = 0 # find 游标从 头开始 if sub==len(find): return start - sub #由于start 游标停在最后的匹配字符上,所以我们要减去搜索串find的长度 return -1 #为搜索到
def prefix(item_str): #获取item_str的前缀 prefix_list = [] item_str = item_str[:-1] for x in xrange(1,len(item_str)+1): prefix_list.insert(x,item_str[0:x]) #print 'src='+item_str #print prefix_list return prefix_list def suffix(item_str):#获取item_str的后缀 suffix_list = [] item_str = item_str[0:] for x in xrange(1,len(item_str)+1): suffix_list.insert(x,item_str[x:]) #print 'src='+item_str #print suffix_list return suffix_list
慢慢读,慢慢画吧,同学
这代码应该没什么明显的Bug!
大概,就是这样,以后争取坚持下来写博客!
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总
- C++线性时间的排序算法分析
- C++实现汉诺塔算法经典实例
- PHP实现克鲁斯卡尔算法实例解析