超详细的KMP算法

超详细的KMP算法

一.暴力算法(BF算法)

在这里插入图片描述
假设我们现在要对上面的字符串进行匹配(假设上面那个串是P,下面那个串是T串)。最开始我们看T的前三个串和P是匹配的,第四个发现不匹配于是我们右移一下
在这里插入图片描述
发现不匹配,再右移一步
在这里插入图片描述
一直这样直到
在这里插入图片描述
于是停止,当然也有可能最后没有对应的匹配串。
代码实现:

import org.junit.Test;
public class Main {
    //从P里面找T的匹配串并返回对应索引的位置,如果没有返回-1
    int BF(String P,String T){
        int i=0,j=0,current=0;
        while(j<T.length()&&current<P.length()){
            while(current<P.length()&&j<T.length()&&T.charAt(j)==P.charAt(current)){
                current++;
                j++;
            }
            if(j==T.length())
                return i;
            else
            {
                j=0;
                i++;
                current=i;
            }
        }
        if(j==T.length())
            return i;
        else
            return -1;
    }
    @Test
    public void Test(){
        System.out.println(BF("abaacababcac","ababc"));
    }
}

在这里插入图片描述
算法复杂度O((m-n)*n)=O(m*n-n^2)

二.KMP算法

在看KMP算法前我们需要知道为什么需要KMP算法?BF算法他不香吗?
我们先看个例子:
在这里插入图片描述
像这样的就会无辜的浪费很多事间去比较。于是KMP就诞生了。

1.prefix table(前缀表)

在这里插入图片描述
我们还是来匹配这两个串。
我们看下面的T串"ababc"
他的前缀串为(我们不看他本身)

编号前缀串
1a
2ab
3aba
4abab

现在我们需要找到每个前缀串他的前缀与后缀(不考虑串本身)相等的最大长度
于是

前缀串长度
a0
ab0
aba1
abab2

接下来就是我们的前缀表:

索引T串前后缀相等最大长度
0a-1
1b0
2a0
3b1
4c2

上面这个前缀表我们可以看成一个数组,取名为Pre;大小为5
那么pre[i]的含义是:

0~(i-1)这i个字符的前后缀相等最大长度,特别的当i=0时我们赋值为-1来区分

2.前缀表的作用

还是前面的P串与T串
在这里插入图片描述
最开始从P[0]与T[0]对齐,开始匹配发现P[3]与T[3]不匹配,于是我们看Pre[3]=1,那么就把T[1]与P[3]对齐再来比较:
在这里插入图片描述
从新的对齐位置P[3]与T[1]开始往后比较(也即绿线处那里),不匹配,我们看Pre[1]=0,于是我们把T[0]与P[3]对齐
在这里插入图片描述
这时从上面的绿线处开始比较,往后比发现T[1]与P[4]不匹配了,这时Pre[1]=0,于是P[4]与T[0]对齐
在这里插入图片描述
这时从上面的绿线处开始比较,发现T[0]与P[4]不匹配了,这时Pre[0]=-1,于是P[4]与T[-1]对齐,而实际上T[-1]使我们虚构出来的,实际上是T[0]与P[5]对齐了
在这里插入图片描述
这个时候已经匹配上了,当然在这里还没结束,我们确实找到了第一个,但是后面可可能还有,因此下一步我们应该看T匹配完成的最后一个字符即T[4],这里Pre[4]=2,于是我们T[2]与P[9]对齐

这时继续前面的操作,最后到这里才是真正的结束了,现在我们对KMP算法已经了解了

3.代码实现

实际上这里的Pre数组就是我们常说的next数组

import org.junit.Test;
/**
 * @author  jackTan
 */
public class MainTest {
    /**
     * 在串P中匹配T串,只匹配第一个,如果没有就返回-1,否则返回匹配到的P中的索引
     * @param p
     * @param t
     * @return
     */
    int Kmp_Search(String p,String t){
        //第一步获取前缀串
        int []pre = getPre(t);
        //i指向p串的字符,j指向t串的字符
        int i=0,j=0;
        while(i<p.length()&&j<t.length()){
            if(j==-1){
                j++;
                i++;
            }
            while(i<p.length()&&j<t.length()&&p.charAt(i)==t.charAt(j)){
                i++;
                j++;
            }
            if(j==t.length()){
                break;
            }
            else{
                j=pre[j];
            }
        }
        if(j==t.length()){
            return i-t.length();
        }else{
            return  -1;
        }

    }

    /**
     * 该方法获取字符串的前缀表
     * @param T
     * @return
     */
    int[] getPre(String T){
        int []pre= new int[T.length()];
        pre[0] = -1;
        for(int i=1;i<T.length();i++){
            pre[i]=getPreOne(T,i);
        }
        return pre;
    }

    /**
     * @param T 目标串
     * @param endIndex 表示要获取0-endIndex的前缀值
     * @return
     */
    int getPreOne(String T,int endIndex){
        int length = T.length();
        int i=0;
        for(;i<length-1;i++){
            if(T.charAt(i)!=T.charAt(length-1)){
                break;
            }
        }
        return i;
    }

    @Test
    public void Test(){
        System.out.println(Kmp_Search("abaacababcac", "ababc"));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值