KMP

1.背景:KMP是由3个外国人想出来的设计的线性时间字符串匹配算法。时间复杂度很低O(n),是判定字串的一个十分简便的方法。

2.运算步骤:假定1个字符串A,对字符串A匹配A的子串,求出一个数组next,通过预算减少了运算的时间,其中next[i]表示了A中以i结尾的的非前缀字串,非前缀字串很好理解的,就比如bread的前缀字串有b,br,bre,brea;(如果字符串A为a,那么A的字串为空)然后将以i为结尾的非前缀字串与A的前缀能匹配的最大长度,就是next[i]=max{j},其中i<j且A[i-j+1~j]=A[1~j];然后对A与B2个字符串相匹配,求出数组f,其中f[i]表示B中以i结尾的字串与A的前缀能够匹配的最大长度,就是f[i]=max{j},其中j≤i且B[i-j+1~i]=A[1~j];这样对字串进行优化。

3.KMP是如何提高计算的速度的?

这个十分容易理解,比如给一个字符串A,里面是abcabcabccc,用另一个串B去比较,假设B为abcabcc,如果按照原始枚举算法,就要从第一个字符开始比较到最后一个字符,这种时间复杂度就是很麻烦的,但是如果提前记录A的子串,再寻找B与A子串相同的部分,就可以极大的减少时间,设想一下,给你一个abcabcabc……循环的好长好长字符串,再给你一个abcd,这样你就会发现,用枚举的方法,这个超级无比慢,但是你用KMP算,发现abc相同,你只需要移动3个位置,便可以从这个超级超级长的字符串中寻找你需要的东西,然后发现没有输出0,这个例子就很好的说明了KMP的效率极其之高。

4.next数组的求法:

①理论:初始化next[1]=j=0,假定next[1~i  -1]已经求出,那么便可以求出next[i]。然后继续扩展next的长度,如果扩展到下个字符不同的时候,令此时的j变为next[j],直到j为0重新匹配。如果可以扩展成功,令j++,next[i]的值就是j。

②代码:

 1 int KMP_search(char* s, char* p)  
 2 {  
 3     int i = 0;  
 4     int j = 0;  
 5     int sLen = strlen(s);  //定义长度 
 6     int pLen = strlen(p);  //定义长度 
 7     while (i < sLen && j < pLen)  
 8     {  
 9         
10         if (j == -1 || s[i] == p[j])  
11         {  
12             i++;  
13             j++;  
14         }  //①如果j = -1,也可以说字串的匹配成功,那么就让i++,j++      
15         else  
16         {  
17             j = next[j];  
18         }  
19     }  //②如果j != -1,并且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j],就是我前面进行了很简单的解释      
20             //next[j]即为j所对应的next值 
21     if (j == pLen)  
22         return i - j;  //这个返回值非常朴素,基本都能看懂我就不解释了 
23     else  
24         return -1;  
25 }  
复制代码

5.f函数的求法:

①理论:和next数组的求法是一样的,代码也差不多,所以就不做多的废话了。

6.递归求next数组:

(我认为这个方式学过递归和字符串的人都比较容易理解,理论呢就不多说了,和next求法是一样的,递归的方式可以去看递归,所以我话不多说直接代码)

 1 void howtofindnext(char* p, int next[])  
 2 {  
 3     int pLen = strlen(p);//同样是对长度进行定义  
 4     next[0] = -1; 
 5     int k = -1;   
 6     int j = 0;  
 7     while (j < pLen - 1)//如果j的大小小于了子串的长度,那么执行循环,大了怎么可能是字串嘞  
 8     {  
 9         //p[k]表示前缀,p[j]表示后缀,前后缀我前面已经有所解释了 
10         if (k == -1 || p[j] == p[k])  
11         {  
12             ++j;  
13             ++k;  
14             //较之前next数组求法,改动在下面4行,这十分重要的!!!!! 
15             if (p[j] != p[k])  //前缀与后缀不同 
16                 next[j] = k;  
17             else  
18                 //因为不能出现p[j] = p[next[j]],所以当出现时需要继续递归,k = next[k] = next[next[k]]  
19                 next[j] = next[k];  //进行下一次递归 
20         }  
21         else  
22         {  
23             k = next[k];  //这个递归方式较上次的next比较容易写,但是我个人认为,上种方法比较容易理解和掌握 
24         }  
25     }  
26 }  

就是这样结束了,我觉得新手也可以看的懂哦!!!

经典例题:poj 3461、poj 2752、poj 2406、poj1961

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值