KMP字符串模式匹配算法JAVA

给定一个目标串,一个模式串。判断目标串是否包含模式串,返回目标串开始匹配的地址。

BF算法

BF算法采用穷举,每次不等时目标串 i加1,模式串j回退到0。平均时间复杂度为O(M*N)

static int bf(String yuan, String target){
        char[] yuanChar = yuan.toCharArray();
        char[] targetChar = target.toCharArray();
        int i = 0, j = 0;

        while (i < yuan.length() && j < target.length()){
            if (yuanChar[i] == targetChar[j]){
                i++;
                j++;
            }else {
                // i-j表示之前已经发生过的不相等情况
                i = i-j +1;
                j = 0;
            }
        }
        if (j == target.length()){
            return i-target.length();
        }else {
            return -1;
        }
    }

KMP

KMP算法是BF算法的优化,主要减少了在目标串中下标i的比较次数。模式串下标j还是会每次归零。
例如:
S串 a a a a b
T串 a a a b

第一轮比较从S[0]、T[0]开始,比较到S[3]、T[3]时,值不相等。
在这里插入图片描述
按照BF算法,这时要从S[1]、T[0]再次开始比较。

可以看到S串、T串前三位是相等的。
S[0] = T[0] S[1] = T[1] S[2] = T[2]

在T[3] = b 的前面的三个字符是a a a,a a a 的前两位是 a a,后两位也是a a。
根据T[0] = T[1] = T[2]还有上面得到的相等信息,可以得到如下图所示的信息。

在这里插入图片描述
按照BF算法,就要拿S[1]和T[0]开始比较。由上图,完全可以从S[3]、T[2]开始下一轮的比较。

KMP算法核心:next数组

KMP使用next[j]来保存在T[j]字符前有多少个字符和T串开头的多少个字符相等,也就是最大前后缀的长度next数组是KMP算法的核心

例如 a a a的最大前后缀是 a a
a b a b的最大前后缀是 a b

默认next[0] = -1 ,表示不保存匹配信息。

T串 a a a b的next数组如下:
在这里插入图片描述
在有了next[j]数组后,当S[i]和T[j]不匹配时,拿S[i]和T[next[j]]进行比较,这就是KMP算法主体。

先看下KMP算法、求next数组的代码实现:

static int is(String yuan, String target){
        int[] next = getNext(target);
        char[] yuanChar = yuan.toCharArray();
        char[] targetChar = target.toCharArray();
        int i = 0, j = 0;

        while (i < yuan.length() && j < target.length()){
            if (j == -1 || yuanChar[i] == targetChar[j]){
                i++;
                j++;
            }else {
                j = next[j];
            }
        }
        if (j == target.length()){
            return (i - target.length());
        }else {
            return -1;
        }
    }


    public static int[] getNext(String target)
    {
        char[] targetChar = target.toCharArray();
        int[] next = new int[targetChar.length];

        // 初始条件
        int j = 0;
        int k = -1;
        next[0] = -1;

        // 根据已知的前j位推测第j+1位,length需要-1 防止越界
        while (j < targetChar.length - 1)
        {
            if (k == -1 || targetChar[j] == targetChar[k])
            {
                next[++j] = ++k;
            }
            else
            {
                k = next[k];
            }
        }

        return next;
    }

给出一个新的模式串并结合代码来看next数组的实现:
a b a c a b a b d
首先 初始化k = -1,k这个变量的意义就是最大前后缀的大小。

next数组代码if分支next[++j] = ++k的意义

求next[3]

在这里插入图片描述
在这里插入图片描述
进入while循环时,k = 0 ,j = 2。k是next[2]的值,是ab的最大前后缀。
在这里插入图片描述
k = 0 表示ab还没有匹配到,当下一个字符T[2] = a过来时,拿T[K]也就是开头还没有匹配到的字符和T[j]判断。如果相等,显然aba就有了最大前后缀 为1。所以next[3] = 1;

if分支k=next[k]的意义

求next[8]
在这里插入图片描述

(1)进入while循环时,k = 3, j = 7。
T[3] != T[7] c != b
在这里插入图片描述
(2)
在j = 7 时,next[7] = 3; 开头三位是匹配的①等于②
在j = 3 时,next[3] = 1;开头一位是匹配的③等于④
根据以上条件可以得到⑤等于⑥ ,③等于⑥。这时候有一位匹配了,也就是next[k =3]的值,只需要拿③下一个字符去匹配T[7]的b。

经过k=next[k],k 值修改为1并再次回到while循环判断。最后T[1] = T[7],所以next[8] = 2
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值