KMP算法理解

kmp算法理解

暴力匹配

原理

在字符串的匹配问题中,被匹配的字符串为”主串“,匹配的字符串为”模式串“,如果最终在主串中有模式串的出现,则返回其具体位置,反之则返回-1(遍历主串的指针为i,遍历模式串的指针为j)。

在暴力算法中,就是直接遍历主串的每个元素,将模式串中的元素一一匹配。

在这里插入图片描述
如该动图所示,该匹配过程为,从主串的第一个元素开始,依次遍历,直至匹配完成,此时返回匹配到的主串第一个元素位置。

代码

public static int bf(String ts, String ps) {
    char[] t = ts.toCharArray();		//主串
    char[] p = ps.toCharArray();		//模式串
    int i = 0; // 主串的位置
    int j = 0; // 模式串的位置
    while (i < t.length && j < p.length) {
       if (t[i] == p[j]) { // 当两个字符相同,就比较下一个
           i++;
           j++;
       } 
       else {
           i = i - j + 1; // 一旦不匹配,i后退
           j = 0; // j归0
       }
    }
    if (j == p.length) {
       	return i - j;		//如果找到,返回在主串中第一个字符出现的下标
    } 
    else {
     	return -1;
    }
}

kmp算法

特点

分析暴力法我们发现,在每次匹配过程中,只要出现不匹配的情况,模式串都是整体和下一个主串元素开始匹配,在这过程中,我们没有从之前的匹配过程中总结规律以便进行之后的匹配,只是机械的遍历匹配,具体表现为:

  • 主串将会回溯
  • 模式串将会归零

那么kmp算法所做的就是处理这两处问题,简而言之,kmp算法的特点为:

  • 利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置

分析实现

那么我们需要解决的问题也就清晰了:当某一个字符与主串不匹配时,我们应该知道j指针要移动到哪

接下来分析j的移动规律:
在这里插入图片描述
C和D不匹配了,此时i不动,j将会移动到第二位"B"处与此时的"C"匹配

因为我们发现,i所指的前一位是"A"可以与模式串匹配
在这里插入图片描述
此时发现一条规律:当匹配失败时,j要移动的下一个位置k。最前面的k个字符和j之前的最后k个字符是一样的。

之前我们第一次成功匹配的是"ABA”,然后第二次成功匹配的是"A",此时j=2(如果第一位定义了j=0,那此时j=1),那么也就是说k=2,最前面的k个字符也就是"A"是和之前"ABA"中的第一个"A"是一样的

用公式表示:P[0—K-1] == P[J-K—J-1]
(j-k:之前匹配成功的主串中第一个元素位置 j-1:之前匹配成功的最后一位即匹配失败前一位)

next数组

上文已经搞明白我们是怎样减少无谓步骤的,那现在该怎么求这个(这些)k呢?

因为在P的每一个位置都可能发生不匹配,也就是说我们要计算每一个位置j对应的k,所以用一个数组next来保存,next[j] = k,表示当主串与模式串不匹配,T[i] != P[j]时,j指针的下一个位置。

  • 代码
public static int[] getNext(String ps) {
    char[] p = ps.toCharArray();
    int[] next = new int[p.length];
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < p.length - 1) {
       if (k == -1 || p[j] == p[k]) {
           if (p[++j] == p[++k]) { // 当两个字符相等时要跳过
              next[j] = next[k];
           } else {
              next[j] = k;
           }
       } else {
           k = next[k];
       }
    }
    return next;
}

kmp主体代码

public static int KMP(String ts, String ps) {
    char[] t = ts.toCharArray();
    char[] p = ps.toCharArray();
    int i = 0; // 主串的位置
    int j = 0; // 模式串的位置
    int[] next = getNext(ps);
    while (i < t.length && j < p.length) {
       if (j == -1 || t[i] == p[j]) { // 当j为-1时,要移动的是i,当然j也要归0
           i++;
           j++;
       } else {
           // i不需要回溯了
           // i = i - j + 1;
           j = next[j]; // j回到指定位置
       }
    }
    if (j == p.length) {
       return i - j;
    } else {
       return -1;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值