KMP算法

KMP算法

一、理论

  参考于:http://www.cnblogs.com/hxsyl/p/3300344.html

  先介绍前缀数组是如何产生的。首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

    来看一个例子:chi表示模式串的前i个字符组成的前缀, next[i] = j表示chi中的开始j个字符和末尾j个字符是一样的(注意下标是字符数目),而且对于前缀chi来说,这样的j是最大值。next[i] = j的另外一个定义是:有一个含有j个字符的串,它既是chi的真前缀,又是chi的真后缀。 

    规定:next[1] = next[0] = 0,这个规定不像0!=1那样,而是确实是这样子,不懂得看上面的前后缀概念。注意:next数组里并不是首尾回文串,而是前缀等于后缀,理解这个对于递推求next数组很重要哟。next[i]就是前缀数组,下面通过1个例子来看如何构造前缀数组。 

     例:cacca有5个前缀,求出其对应的next数组。前缀2为ca,显然首尾没有相同的字符,next[2] = 0,前缀3为cac,显然首尾有共同的字符c,故next[3] = 1,前缀4为cacc,首尾有共同的字符c,故next[4] = 1,前缀5为cacca,首尾有共同的字符ca,故next[5] = 2。如果仔细观察,可以发现构造next[i]的时候,可以利用next[i-1]的结果。比如abcdabc,模式已求得next[7] = 3,为求next[8],可以直接比较第4个字符和第8个字符,如果它们相等,则next[8] = next[7]+1 = 4,这是因为next[7] = 3保证了前缀ch7的末尾4个字符的前3个字符是一样的。但如果这两个字符不想等呢?那就继续迭代,利用(k=3)k = next[k]的值来求,直到k=0(next[8] = 0)或者字符相等(next[8] = k+1)。

二、例子与实现

  已知模式字符串P[1..m]与文本字符串T[1..n],且已知模式字符P[1..q]与文本字符T[s+1..s+q]匹配,那么满足:P[1..k]=T[s1+1..s1+k],其中s1+k=s+qk是满足条件的最大值,如下图所示。

 

  从图中可以看出,如果P[q+1]T[s+q+1]不匹配,无需把P[1..k]T[s1+1..s1+k]的字符先比较,由公式P[1..k]=T[s1+1..s1+k]可知它们肯定是匹配的,同样如果拿P[k+1]T[s+q+1]比较,如果不匹配,则用k中的前k1(k1满足P[1..k1]=T[s2+1..s2+k1])个当做k,来比较P[k+1]T[s+q+1],一直下去,直到P[k+1]= T[s+q+1]

可以用模式与自身进行比较,在指定匹配长度为q的条件下,预先计算出k的值。

具体实现:

  q为匹配字符的长度,k为满足等式中P[1..k]=T[s1+1..s1+k]的前缀最长长度值。prefix_func[]存放在匹配了长度为q的条件下k的值。通过prefix_func[],就可以依据相同的算法获得P对T的模式匹配,如下面的KMP算法。

 1 int next[100];
 2 
 3 void GetNext(char *t)
 4 {
 5     int i = 0, j = -1;  //注意j=-1
 6 
 7     if (t == NULL)
 8         return;
 9     next[0] = -1;
10     while (t[i])
11     {
12         if (j == -1 || t[i] == t[j])  //为了获得next[i+1],默认next[1]=0    
13         {
14             i++;
15             j++;
16             next[i] = j;
17         }
18         else
19             j = next[j];
20     }
21 }
22 int Kmp(char *s, char *t)
23 {
24     int i = 0, j = 0;  //注意i和j的初值
25 
26     if (s == NULL || t == NULL)
27         return -1;
28 
29     while (s[i] && (j == -1 || t[j]))
30     {
31         if (j == -1 || s[i] == t[j]) //j==-1说明s[i]开头的字串不可能和t匹配,所以s直接到下一个字符去,理解这个很重要  
32         {
33             i++;
34             j++;
35         }
36         else 
37             j = next[j];
38     }
39     if (!t[j])
40         return (i - j);
41     return -1;
42 }

next[i](假设next[i]=k)记录的是P[0~k]==P[i-1-k~i-1],这点和前面的理论稍有不同。

posted @ 2013-01-19 16:43  在于思考  阅读(810)  评论(3编辑  收藏  举报