KMP算法小节

 KMP算法通过预处理待搜索的已知字符串来得到一组其本身组成信息的数据,并用该数据在匹配搜索中对如何进行匹配、移动进行指导。

直观来看,设待搜索串为L{i,...,j},长度为M;同时设进行搜索的主串为H{k,。。。,p},长度为N。我们订好串之间的长度限制我们都是满足的。

在H中进行匹配搜索的时候,不失一般性,设匹配到任意一点m(0=< m < M)后,m+1点不匹配,这是只能把L的起点在H中向后推移重新开始匹配;最简单的就是后移一位,从头开始,这样的复杂度是O(NM)。
为了加快速度,我们希望能后通过一些已有的信息来帮助我们,对这样的任意一点m,我们如果能移动多移动几位,那么搜索的速度就会加快很多;在解释KMP的预处理过程之前,我们来直观的看一下实际上我们的需要。
从1-m点已经匹配,我们要移动,不失一般性,假设移动n位正好能满足既不漏掉需要搜索的解,又满足了移动最大可能的位数;这样的一个n有什么样的直观意义呢?
a1.......am(主串H)                                a1.......am(主串H)    
b1.......bm(串L的前m位)                              b1.......bm(串L的前m位),这里am和bm差就是这个“正好”的n

那这样的n必然表示从b1到am之间的这段一定和从bm点开始向后的同样长的一段字串相同;而这个信息,正KMP算法通过预处理L来得到的每一点对应的移位长度nl;这样在搜索过程中,每一点上我们都知道应该移位几个,直至到达主串长度为止;这样既没有漏掉任何一个可能的解,同时也充分利用L的特性加快的搜索的速度.



每个算法都适用性的范围,有时我们可以考虑别的算法,比如BM算法等。

本文的目的就是想通过最直观的方式来解释一下KMP的机理,每种算法都有直观的解释,这种解释方法很好很强大呵呵。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 俺是分割线

呵呵,再加上一点吧;前边说了我们通过预处理L能够得到加快搜索的信息数据,那么这个预处理的过程是怎么样做呢?
最简单的方法,对L中的每一点,都从头开始匹配一次,这样的复杂度比较高,O(n的平方),O(n的三次方方)等。

想加快速度,好;那我们肯定是要在处理每一点的时候能够用到已经处理过的前边的点的信息,其实算法分析一般都是这样,想提高速度,优化空间,总是要利用已有的信息(空间),考虑它们之间的关系和发展变化的情况,想想是不是还有可利用的信息来优化。

现在不失一般性,我们考虑m点已经匹配,那么m点所对应的移位应该是多少呢?能不能通过已有的1 - m-1点的信息来得到呢?如果可以,预处理算法也许就是O(N)级的复杂度了。
根据假设,m-1点的位移n-1我们已经知道,也就是说在m-1这个点上,有从点1开始的(m-n)的一个字串和从m-1开始向后的同长的串相等;那么现在我们考虑m点,我们当然首先判断从1开始的第(m-n+1)的这个点是不是和m点相同,如果相同那么m点的位移就比m-1多了一个为n;这个很好理解对吧,那么如果不相等呢?都知道要缩短判断的范围,那么这个范围从哪里知道呢?

很直观的,我们现在考虑m位,假设的前提是已经匹配到了m位,m+1位和对应位置的不同;不管三七二十一,我们先把串向后移动n位,因为我们知道至少要移动这么多; 这是从1开始的(m-n)这个点正好对着主串上原本m-1点对着的,而从1开始的(m-n+1)这个点对着原来的m点 --但这两个点不相等!!
好从现在开始,我们又要考虑需要继续移动几位!m点不等,现在匹配了的长度只有m-1!好,这就简单了,又回到开始的假设了,m-1点匹配了后需要移动几位?这个是已知的了,呵呵,所以这样我们通过这样的查找,很快的就找到因该移动几位,而不需要挨个试,通过利用前边已有的信息来得到当前的信息。伪代码如下:

//
// dst是需要进行预处理的串,mov是一个长度等于dstlen的数组用来存放dst中每一点m对应的前边相同子串
// 的对应的那个点j,也就是说 m - j就是需要移动的距离
//
void prekmp(const char *dst, int *mov, int dstlen)
{
         mov[0] = -1;
         for (int i = 1; i < dstlen; i++) {
                  int      j;

                  j = i - 1 ;
                  while (j >= 0 && dst[i] != dst[mov[j ] +1] ) {
                             j = mov[j ] ;
                  }

                   if (j < 0)
                           mov[i] = -1;
                   else
                          mov[i] = mov[j ] + 1;
         }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值