KMP优化字串

Posted sutianhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }  

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

以上是关于KMP优化字串的主要内容,如果未能解决你的问题,请参考以下文章

算法 - KMP算法

hdu 4333 扩展kmp+kmp重复字串去重

hdu-4763(kmp+拓展kmp)

poj2406 kmp 求最小循环字串

KMP算法分析

POJ - 2406 ~SPOJ - REPEATS~POJ - 3693 后缀数组求解重复字串问题