KMP算法

问题

已知某个主串和子串,现要求求出字串p第一次出现在主串s中的位置。

算法讲解

这一算法看了很多材料终于搞懂,非常不容易。无论是教材还是其他博客,演示过程太过抽象,推荐以下视频教程:

  • https://www.bilibili.com/video/av49930100?t=664
  • https://www.bilibili.com/video/av91575042?t=1670&p=82 (P82 P83)

算法思想

算法的核心在于准确理解next[]数组的含义。next[j]的含义是:当字串p在第j位失配(字符不匹配)时指针j所要跳转到子串p的位置。

求next数组:比较j和k上的字符是否相同,

  • 不同 则 k = next[k] 一直回溯直到Pj = Pk或者k<0
  • 相同 则 next[j+1] = k+1
    (求next数组是一个递推的过程,要想求得第j的k值(即:next[j] )则需要参考next[j-1]在其基础上比较j -1和 next[j-1]的字符是否相同求得,如果相同,则在其结果上加1,k值就一直回溯,直到k小于0.)

逻辑如下:
如果 Pj = Pk , next(j+1) = k+1 或者 k<0 时 ,next(j+1) = k+1; j++; k++;
否则:k继续回溯直到满足 Pj = Pk 或者 k < 0.

在这里插入图片描述

求解

  • 暴力算法
private static int indexOf1(String s, String p){
        int i = 0;
        int j = 0;
        int sc = i;   //扫描指针每次从i开始
        while(sc<s.length()){
            if(s.charAt(sc)== p.charAt(j)){
                sc++;
                j++;
                if(j==p.length()){
                    return i;
                }
            }else{  //在某一位不匹配
                i++;  //主串移动到下一位
                sc = i;  //回溯到i的下一位
                j = 0;  //字串回溯到0 从新开始
            }

        }
        return -1;
    }
  • KMP算法
public class KMP {
    public static void main(String[] args) {
        String src = "babababcbabababcba";
        int index = indexOf(src, "ababc");
        System.out.println(index);
    }
    private static int indexOf(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        int[] next = next(p);
        int i = 0;
        int j = 0 ;
        while(i<sLen){
            //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
            //j=-1,因为next[0]=-1,说明p的第一位和i这个位置无法匹配,这时i,j都增加1,i移位,j从0开始
            if(j==-1||s.charAt(i) == p.charAt(j)){
                i++;
                j++;
              
            }else{
                //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
                //next[j]即为j所对应的next值
             
                j = next[j];   //第j位失配了,j指向其他位置
            }
              if(j==pLen){//匹配成功
                    return i - j;
                }
        }
        return -1;
    }
    private static int[] next(String p){
        int pLen = p.length();
        int[] next = new int[pLen];
        next[0] = -1;
        if(p.length()==1){
            return next;
        }
        next[1]=0;
        int j = 1;
        int k = next[j];//看看位置j的最长匹配前缀在哪里
        
        while( j<pLen -1){
            if( k<0 || p.charAt(j) == p.charAt(k)){
                next[++j] = ++k;   //现在要推出next[j+1],检查j和k位置上的关系即可
            }else{
                k = next[k];
            }
        }
    return next;
    }
}

算法比较

  • 暴力算法 时间复杂度 O(m*n)
  • KMP 时间复杂度 O(m+n)

算法扩展

题目:已知某个主串和子串,现要求求出子串p出现在主串s中的次数。
代码在上面代码基础上改造如下:

    private static int indexOf(String s, String p) {
        int sLen = s.length();
        int pLen = p.length();
        int[] next = next(p);
        int i = 0;
        int j = 0 ;
        while(i<sLen){
            if(j==-1||s.charAt(i) == p.charAt(j)){
                i++;
                j++;
            }else{
                j = next[j];   //第j位失配了,j指向其他位置
            }
            if(j==pLen){//匹配成功
               // return i - j;
                count++;
                i--;
                j = next[j - 1];
            }
        }
        return -1;
    }

运行展示

测试代码如下:

public static void main(String[] args) {
        String src = "babababcbabababcba";
        int index = indexOf(src, "bab");
        System.out.println(count);
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值