字符串匹配->KMP算法

背景:最近在学习数据结构,学到“串”这一块时,遇到了KMP算法。但是卡了好长时间不是很理解这个算法。今天突然get到这个算法的点了,故写博客记录一下,防止之后遗忘。可能我理解还有什么不到位的地方,如果大家发现了请多多指教。

为了方便叙述,先设定被搜索串(主串)为M,而搜索串(目标串)为T。

关于串的匹配模式,最简单最常用的也就是BF算法了,它的原理就是每次从主串M中取出一个和T等长的子字符串,如果匹配,就返回串的起始位置,如果不匹配,则重新从M中取一个子字符串再次比较。直到找到匹配的字符串或者是搜索完主串但并未找到目标串,搜索结束。代码如下:

int BF(String M, String T){
        int m = M.length();
        int n = T.length();
        for (int i = 0; i < m - n; i++) {
            String temp = M.substring(i, i + n);
            if (temp.equals(T)) {
                return i;
            }
        }
        return -1;
    }

正如其名,BF用暴力的手段去查找字符串是否匹配,虽然这是大多数最容易接受的一种方法,但是效率明显低。

现在举个例子主串为“zxcvzbznmasdf”,目标串为“znm”。如果采用BF算法,则会先从主串中拿出“zxc”和目标串比较,发现不匹配,接着再拿出一个字符串“xcv”去比较,以此类推。

但是如果我们自己去比较的话,先从目标串中拿出“zxc”,发现不匹配。接着我们会怎么做呢?肯定不会再去取“xcv”来和目标串比较,因为我们已经知道“xcv”中的后两个字符一定不会和目标串匹配。(我当时就是卡在这个地方的,具体为什么我们再接着讨论)总之,我们知道之前的BF肯定效率低的,因为中间可能有一些子字符串并不需要比较,因为明显不会匹配。

接着,我们再来介绍KMP算法,这个算法的背景就不介绍了,感兴趣的自行百度。

KMP算法引入一个next数组,该数组记录的是目标串的每个位置之前的子字符串(不包括该位置)的相同前缀和后缀的最大长度。

举个例子,目标串“zxcdzxce”,next[0]表示位置0前的子字符串“”的相同前缀和后缀的最大长度,为0;next[4]表示位置4之前的子字符串“zxcd”的相同前缀和后缀,为0;next[5]表示位置5之前的子字符串“zxcdz”的相同前缀和后缀,即“z”的长度,为1;以此类推,得到所有的next数组元素值。

现在我们在俩考虑前面红字部分的困惑。假设假设主串为“zxcdzxcefdsewa”,目标串为“zxcdzxcr”,第一步,我们先从目标串中取出“zxcdzxce”,发现只有最后一个字母不匹配,其对应的next数组为next[7],该位置最长的相同前缀和后缀为“zxc“,值为3。此时,我们知道目标串中前7个字母和主串中的前7个字母是匹配的,而这7个字母组成的字符串的最大相同前缀和后缀是”zxc“,所以,我们会直接从主串的第二个“zxc”开始比较,因为我们知道,这两个“zxc”之间没有“zxc”,肯定不会匹配目标串的,没有必要采用BF算法,因此提高了效率。(这块大家需要慢慢理解

接着,我们再来讨论next数组怎么求,这个是整个KMP算法的核心部分。这里先贴出最常用的代码:

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

    // 初始条件
    int j = 0;
    int k = -1;
    next[0] = -1;
 
    // 根据已知的前j位推测第j+1位
    while (j < strKey.length - 1)
    {
        if (k == -1 || strKey[j] == strKey[k])
        {
            next[++j] = ++k;
        }
        else
        {
            k = next[k];
        }
    }

     return next;
}

关于这个代码的具体实现原理,我是参照别人的博客理解的,他的博客中用图像解释了代码,很容易理解,这里贴出链接:https://www.cnblogs.com/tangzhengyue/p/4315393.html

最后,我们再来用代码实现KMP算法:

public int KMP(String M, String T){
        int m = M.length();
        int n = T.length();
        char[] ms = M.toCharArray();
        char[] ns = T.toCharArray();
        int[] next = getNext(T);
        int i = 0, j = 0;
        while(i < m && j < n){
            if (j == -1 || ms[i] == ns[j]){
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == n)
            return i - j;
        else
            return -1;//表示不存在
    }

之后就可以在编译器中带入具体字符串去实验,理解算法。学习这个算法是先了解算法的原理,接着在仔细考虑具体的代码实现原理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值