KMP算法的原理实现

KMP算法是什么?

KMP算法是D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。

目的:为了解决模式串匹配主串的时间复杂度最小,通俗的讲,是有一个字符串A(主串),给定另外一个字符串B(模式串),求A串含有B串的位置

在讲KMP算法之前,首先要知道前缀跟后缀的定义

前缀:有一个n个字符的字符串,前n个字符都是前缀。

eg:abcabc ,这个字符的前缀有a,ab,abc,abca,abcab ,5个前缀,最长一个是abcab

后缀:有一个n个字符的字符串,后n个字符都是后缀。

eg:abcabc ,这个字符的前缀有c,bc,abc,cabc,bcabc ,5个前缀,最长一个是bcabc

上面两个例子,有相同并且最长的前后缀是abc

 

KMP有两个关键的地方,解决这两个,就解决了KMP算法

1.模式串跟主串是怎么匹配的?

2.为模式串建立匹配表

 

一,我们先来看第一个问题,模式串跟主串是怎么匹配的?

假设

A串(主串):ckabawababab

B串(模式串):ababab

我们凭第一感觉,想一下应该是这样匹配的,首先A串的第一个c跟B串的第一个a比较,发现不同,A串下移一位k,再与B串a比较,发现又不同,继续A串下移一位a,再与B串a比较,发现相同,这是A,B串同时下移一位,继续比较...,直到发现A串w跟B串的b不相同,这时A串不移动,B串回到原来的第一位,又重新开始比较,依次类推,最后算得结果ababab

这种方法有一个缺点,就是B串模式串老是重复计算,效率太低。

接下来,看KMP算法是怎么做的?

一开始,跟之前的做法一样,直到发现A串w跟B串的b不相同的时候,这时我们需要看B串b之前的相同最长前后缀是什么,就是分析aba的前后缀,是a,这是我们不必要将B串模式串移动到原点,只需移动到B串第二位b那里,为什么呢?

因为A串w前面的a等于B串第三位的a,又因为B串第三位的a跟B串第一位的a是相同前后缀,所以B串第一位的a必然等于A串w前面的a。

依次类推,最后得出结果

 

二,接下来,我们来解决第二个问题,为模式串建立匹配表

模式串:ababab

我们用一个临时数组A来存储模式串的匹配表

首先模式串第一位字符a前面,没有相同前后缀,即0

表示 A[0] = 0 ,

模式串第二位字符b前面,没有相同前后缀,即0

表示 A[1] = 0 ,

模式串第三位字符a前面,有相同前后缀a与a,前缀末位a从下标0开始,下标加一,这样表示相同前后缀的长度

表示 A[2] = 1,

模式串第四位字符b前面,有相同前后缀ab与ab,前缀末位b从下标1开始,下标加一,这样表示相同前后缀的长度

表示 A[3] = 2,

模式串第五位字符a前面,有相同前后缀aba与aba,前缀末位a从下标2开始,下标加一,这样表示相同前后缀的长度

表示 A[4] = 3 ,

模式串第六位字符b前面,有相同前后缀abab与abab,前缀末位b从下标3开始,下标加一,这样表示相同前后缀的长度

表示 A[5] = 4,

所以A数组=[0,0,1,2,3,4]

//创建匹配表
function createPattern() {
    var str = "ababab";
    var prefix = [];
    var subfix = [];
    var patternMatch = [];
    //匹配表
    for (var i = 0; i < str.length; i++) {
        var newstr = str.substring(0, i + 1);
        if (newstr.length == 0) {
            patternMatch[i] = 0;
        } else {
            for (var k = 0; k < i; k++) {
                prefix[k] = newstr.slice(0, k + 1);
                subfix[k] = newstr.slice(-k - 1);
                if (prefix[k] == subfix[k]) {
                    patternMatch[i] = prefix[k].length;
                }
            }
            if (!patternMatch[i]) {
                patternMatch[i] = 0;
            }
        }
    }
    return patternMatch
}

 

//KMP算法完整代码
function KMP() {
    var str = "aabaabaaa";
    var sourceStr = "bbbadcadcadcaaabaabaaa";
    var prefix = [];
    var subfix = [];
    var patternMatch = [];
    //匹配表
    for (var i = 0; i < str.length; i++) {
        var newstr = str.substring(0, i + 1);
        if (newstr.length == 0) {
            patternMatch[i] = 0;
        } else {
            for (var k = 0; k < i; k++) {
                prefix[k] = newstr.slice(0, k + 1);
                subfix[k] = newstr.slice(-k - 1);
                if (prefix[k] == subfix[k]) {
                    patternMatch[i] = prefix[k].length;
                }
            }
            if (!patternMatch[i]) {
                patternMatch[i] = 0;
            }
        }
    }

    console.log(patternMatch, "匹配表");
    //循环比对
    var j = 0;
    var result =''
    for (var i = 0; i < sourceStr.length; i++) {
        if (sourceStr.charAt(i) == str.charAt(j)) {
            j++; //模式串下标加一
            if (j == str.length) {
                //比对找到,并返回结果的坐标
                console.log(i - str.length + 1, "结果");
                result = i - str.length + 1
                return;
            }
        } else {
            j = j - patternMatch[j]; //根据匹配表后移动
            if (j < 0) {
                j = 0;
            }
        }
    }
    return result;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值