KMP模式匹配详解

KMP模式匹配详解

首先,在kmp之前已经有一种很简单的算法,叫做朴素算法,时间珍贵,这里就不多解释啦。

我们直接上例子来分析:随便给出文本串 T[]=acdabcdabcdaca ,模式串 s[] =cdac; i表示文本串的下标,j是模式串的下标。
当我们比较的时候,发现比较到以下时

cadabcdabcdaca
adac
此时我们如果说找到了一个串它是模式串的前缀串与后缀串相同的最长的串(比如ababcaba ,前面的aba和后面的aba是一样的,所以此串最大前后相同的串就是aba,一定要是最大哦)

因为我们到文本串的b和模式串的c不匹配了,所以可以确定前面每一个都是匹配的,那么在j位置前模式ada,它的最大相同的串是a。我们可以确定文本串b前的a与模式串c前的a,以及模式串刚开头的a,是一样的了。那么我们接着比较文本串的不匹配的b与模式串a后的字符是不是相同就好了。

所以呢我就想有一个数组存放的是下标为j前的模式串前后相同的最大的串的数值
举个栗子:
bcacbcdabcacaa
这个数组值(本数组下标和j一样)第一位先空着,1,0,0,0,1,2,0……

但是我这个想引入的数组并不能很直观的表示什么,所以我们需要引入一个next数组:这个数组表示当i跟j不匹配了j到底一个跑到哪跟i比较呢。仔细想想是不是发现i好像不用往回走了,往回走的只有j哎。到这里应该感觉到为什么kmp是优化了的。

虽然我之前的数组没有什么实际用途,但是是为next数组服务了,因为next数组的值=我那个数组的值呀。
然后我们来求next数组的值————(上代码了)
思路已经讲清楚,以下代码全靠自己理解了

2	{  
3	    int pLen = strlen(p);  // plen是模式串数组长度
4	    next[0] = -1;     //赋值为-1,一方面可以刚开始的时候直接进入if语句
5	    
     int k = -1;       
	       // k表示前后有几个相同的,如果 p[j] == p[k],k++,要更新next[j]  
        //  值,如果p[j] != p[k],就要从next[k]里面取值。
6	    
      int j = 0;  
7	    while (j < pLen - 1)  
8	    {  
9	        //p[k]表示前缀,p[j]表示后缀  
10	        if (k == -1 || p[j] == p[k])   
11	        {  
12	            ++k;  
13	            ++j;  
14	            next[j] = k;  
15	        }  
16	        else   
17	        {  
18	            k = next[k];  
19	        }  
20	    }  
21	}  

关于为什么要从next【k】里取值呢,我们来分析一下:上栗子:
文本串 abacazcazcazcsnccazcabccaa
模式串 cazcaa

当匹配到
aba cazcaz c azcsnccazcabccaa

   cazcaa

时:z跟最后的a不匹配,按照我们之前的想法,找出模式串最大相同串,比较文本串的z和模式串的最大相同串后一位就好了,发现是匹配的,接着比较发现文本串后来的zca和模式串zca都是一样的,但是z和模式串最后一个a不匹配了,此时我们咋做呢(只能放弃比较了,毕竟比较失败了!哈哈,不可能)但是文本串的zca是和模式串匹配的呀,我们的此时的文本串的i (毕竟i不可能回溯)和谁比较呢?是不是只要找到j位置前最大相同串,然后用最大相同串的后一位接着和i位置比较 。
k位置时,它的最大相同串的后一位就是next【k】,所以我们将j定位到next【k】继续和i位置比较。

然后上总代码:c版
#include <stdio.h>
#include <string.h>

int next[32] = {};
void get_next(char *T, int *next)
{
    int k = -1;
    int j = 0; 
    next[j] = k;
    while (j < strlen(T)) {
        if ( (k == -1) || (T[j] == T[k]) )        {
           ++k; // 注意是先加后使用
            ++j;
            next[j] = k;
        }
        else
        {
            k = next[k]; 
        }
    }
}
 
int index_KMP(char *S, char *T, int pos)
{
    int i;     int j;
    i = pos;
    j = 0; 
    while ( (i < strlen(S)) && (j < strlen(T)) )
    {
        /* j = -1 表示next[0], 说明失配处在模式串T的第0个字符。所以这里特殊处理,然后令i+1和j+1。*/
        if ( (j == -1)  || S[i] == T[j])
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }
    }
 
if (strlen(T) == j)
{
    return i - strlen(T);
}
else
{
    return -1;
}

}

int main(void)
{
    char *s = "ababcabcacbab";
    char *t = "abcac";
    int pos = 0;
    int index;

printf("================ BM ==============\n");
index = index_BM(s, t, pos);
printf("index = %d\n", index);

printf("================ KMP ==============\n");
get_next(t, next);
print_next(next, strlen(t));

index = index_KMP(s, t, pos);
printf("index = %d\n", index);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值