16 - 12 - 20 KMP算法 模式匹配 终极奥义!

尊重产权,转载请注明出处。

ex:找一个单词在一篇文章中的定位 问题 :这种子串的定位操作
通常叫做 串的模式匹配。
ex 我们要从 goodgoogle 中 找 google 这个单词。
朴素的方法是:
1、取子串的第一个字符叫做子首,主串 向后查找,
直到找到第一个与子首相等的字符叫做主首;

2、子首后移一位,主首后移一位,再次比较(重复此过程)
要是直到查找了n次(n = 子串长度),仍然是次次对应相等,
说明子串就是主串真正的子串。

3、比如:
主串:goodgoogle;
子串:google;主串一开始与子串比较,子串比较到 g-d 发现不相等了,指向主串的指针退回到第二个字符o再重复2过程,

ex-2 : (设 n 为主串长度,m 为子串长度):
主串:00000000000000000000000000000000000000000001 n=44
子串:0000000000000000001 m= 19;
需要比较的次数为:(n-m)*m (比较简单)
其实这是完全没有必要的,因为 当比较到 g-d 时候,
我们就能知道前面的六个字符肯定都不符合,
根本不需要退回到主串的第二个字符重新搜索

那,有人会问,干嘛非要讨论这种极端情况呢?
不,其实真的很有必要!
因为当m 越小,出现这种低效匹配的概率越高,
而若当m 越大,所导致的后果也就越严重。

不过,蛮力算法也并不是一无是处,比如在实际情况如搜索引擎里,
n是对应着需要搜索的库,m是用户输入的关键字,
n相对m来说是远远大于m的,所以O(n-m+1)*m 也等于 O(n),
极端情况的概率也就变得很小了。

*************************KMP讲解:****************************

比如:比较 abcdefgab 和 abcdex

仔细观察发现:子串的首字符a 与其后的bcdex 都不相等,
那么对于主串abcdefgab 的 与子串对应相等的前五位而言已经失去了比较的意义。主串只需要直接跳转到f 开始比较就行。
那么主、子串要是这样:
S = abcabcabx
T = abcabx 观察到子串中T1 = T4, 这种情况怎么办呢?
可以确定S2 S3肯定是不用再次比较了,所以T只需要与S4 S5 对齐即可。

子串如何滑动与主串是没有关系的,因为出现失配的前面 肯定都是匹配的,
所以指向主串的计数变量 i 不动,指向模式串(子串)的计数变量 j 会根据next 来确定滑动距离。
下面我们就以失配前匹配的那一段前缀来构建next[ ]数组以期确定下一次子串滑动的方向

那么next 数组究竟启什么作用?

返回失配位之前的最长公共前后缀!


具体来讲,KMP算法想要解决的主要矛盾是子串的自匹配所造成的重复冗余。



↑ 其中一律取 0 :就是从模式串的第一个字符开始 重新比较呗,
模式串中 没有重复的部分,那我们的KMP 也就无用武之地了。

说了这么多,那如何构建一个next[]数组呢?

<<<<<<<<<<代码君>>>>>>>>>>>

int main() {

    int next[10]; int pos = 0;
    char T[MAXSIZE],S[MAXSIZE];
    printf("Please input the main string and the model string \n");
    while(scanf("%s%s",S,T)) {
        Getnext(T,next);
        int result = KMP(S,T,pos,next);
        if(result)
            printf("We found it :%d \n\n",result);
        else
            printf("NO FOUND!v\n\n");
    }


    system("pause") ;
    return 0;
}

这里写图片描述

/* 在主串 S 的第 pos 个位置开始查找子串 T,返回模式串在主串中的位置*/


int Getnext(char *T,int next[]) {
    int j = 0 , k = -1;int tlen = strlen(T);
    next[0] = -1;
    while( j < tlen) {
        if(k == -1 || T[j] == T[k] ) {
            j++;
            k++;
            next[j] = k;
        } else
            k = next[k];
    }
}


int KMP(char *S,char *T,int pos,int next[]) {
    int i = pos - 1;
    int j = 0;
    int slen = strlen(S);
    int tlen = strlen(T);
    while(i < slen && j < tlen) {
        if(j == -1 || S[i] == T[j]) {
            i++ ;
            j++ ;
        } else
            j = next[j];
    }
    if(j >= tlen)
        return i - tlen + 1;
    else
        return -1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值