字符串匹配之有限自动机&kmp算法

  由于传统的字符串匹配效率不高,大概思路:从主串的第pos个字母起和模式的第一个字符比较之,若相等,则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式的字符比较之,以此类推,直到匹配完主串的每一个子串,如果主串有n个字符,模式串有m(m<n),则在最坏情况下是O(n*m)的复杂度。因此考虑到改进传统的字符串匹配算法。

  kmp其实是在有限自动机的基础上改进过来的,因此先考虑有限自动机。它的主要改进点是,当出现主串和模式串某个字符比较时不相等时,不返回到主串上次比较位置的下一个位置,而是通过对模式串进行预处理后,得到一个状态转移表,表中每一个元素是一个状态,根据此表就可以不返回主串的位置,也就是说主串可以一直向后比较,改变标记的只是模式串的位置,也就是下次所在的状态。这样一来在匹配时的时间复杂度就只有n了(主串只扫描了一遍),但是用有限自动机在构建状态转移表时的复杂度是O(m*@),m是他的模式串的长度,也就是所拥有的所有状态数,@就是在这个模式串中出现的字符种类,当建立了这个表,就可以快速的根据当前的字符,和当前的状态找到转移到的状态,伪代码非常的简单:

FINITE_AUTOMATON_MATCHER(T,&,m)

1       n <---length[T]

2       q <----- 0                  //q为当前的状态,即模式串中的位置

3       for i <----1  to  n

4              do     q  <-------  &(q,T[i])    //把表中的状态给q

5              if q == m   then  print " Pattern occurs with shift" i-m;

这就是对只有一个模式串的有限自动机的方法,下面介绍kmp算法,他的改进地方主要在对模式串的预处理上,对于只有一个模式串的问题,可以仅用一个next数组(含有m个元素)来表示,构建时间为m,这样算法的时间就不受限于不同的模式串了,只要他们的长度一样,运行时间就一样。但是kmp也仅限于处理单模式串的匹配问题,对于 多模式串的匹配,还是要回归到原来的有限自动机的方法,这个在最后介绍。

kmp构造next数组最关键的一个思想就是:对于模式串的第j个字符,next[j]的数值等于在前j个模式串中p1,...,pj,找到一个最长的前缀,这个前缀也是此子模式串的后缀,(很显然,此值必然小于j)这个最长的前缀的字符数就是next[j]的数值。伪代码如下:

COMPUTE_PREFIX_FUNCTION(P)
1   m<--- length[p]
2   next[1] <--- 0
3   k<--- 0
4   for q <-- 2 to m
5         do while k>0 and p[K+1] != p[q]
6                 do k <--- next[k]
7            if p[k+1] = p[q]  then k <--- k+1
8             next[q] <--- k
9   return next

求最大前缀的实现巧妙的运用了最大前缀传递的性质,第6步是关键的语句。复杂度m。
有了这个预处理,在进行匹配时就跟之前的有限自动机一样了。这里省略了伪代码。。。他的复杂度为n

最后说一下对于多模式串的匹配。这个我自己也没有实现过,当时刚好我同学要做这方面的东西,我也顺便了解了一下实现过程。对于多个字符串,只能通过有限自动机创建状态转移,当全部建好后,它就类似于一棵字母树,每一条到叶子的路径代表着一个模式串,然后通过广度优先确定当出现匹配不相等时,转移到的状态。这里如果有很多模式串,而且字符的出现概率,等等都是随机的话,这棵字母树就会非常的复杂,需要一个比较好的结构来存储,平衡二叉树(红黑树)在基于时间空间上的权衡后是一个不错的选择,当然还有其它很多的存储方式。。。。。。本人对于字符串的匹配学习主要是通过MIT的算法导论和同学的几篇论文。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值