【算法】KMP经典算法,你真的懂了吗?

有关KMP算法的书籍、帖子、博客铺天盖地,但是你真的能看懂?你知道为什么要有next数组,next数组到底什么意思,又该怎么求next数组,有了next数组之后又该怎样判断模式串和主串是否匹配成功?本文绝对不是讲解KMP算法最细致的一篇文章,但却是为了解决大家的疑惑而写的一篇文章。

KMP的概念

首先说说什么是KMP算法,说白了,就是不希望用简单的两层循环遍历两个串那样去看能否匹配成功。简单朴素的字符串匹配是,一旦匹配不成功,主串要回到匹配开始的起始位置,然后加1再和模式串从头匹配。
这里写图片描述
如此效率太低,有没有效率更高的呢,显然是有的,这就是KMP,KMP算法效率之所以高,是因为主串不用回溯,只要模式串回溯就可以,也就是上面的j=4,s=4发现不匹配之后,j=4不变,s改变,那s到底怎么改变呢,这就是next数组的作用了!

next数组的概念

看过KMP的人都知道字符串前缀和后缀这两个概念,而我并不会说这两个概念。next数组通俗易懂的理解就是,对于模式串中每一个字符,看它前面字符串中存在的前缀和后缀匹配的最长长度。什么意思呢?
这里写图片描述
仔细看,应该可以很快明白过来,所谓的next数组就是求前面串中前后缀相等的最大长度而已。

next数组的作用

尽管知道了next数组,但是它做什么作用呢?知道求这个数组,但是仍然不能知道它有什么意义,那也白搭不是?接下来,便是KMP中的精髓,即next数组有什么作用。请看下图:
这里写图片描述
从上面过程可以知道,next数组就是保存着当主串和模式串不匹配时,接下来与主串j比较的模式串中s的位置,即s=next[s]。

next数组的求法

前面有对next求解的过程,然而那只是为了理解next的含义,真正算法编程却无法那样直观求出,那该如何求解next数组呢?这里用到了类似并查集的算法。
主串:abababbbab

  • 首先next[0]=-1,next[1]=0;
  • 之后每一位j的next求解:
    比较j-1字符与next[j-1]是否相等,
    如果相等则next[j]=next[j-1]+1,
    如果不相等,则比较j-1字符与next[next[j-1]]是否相等,
    1) 如果相等则next[next[j-1]]+1,
    2)如果不相等则继续以此下去,直到next[…]=-1,则next[j]=0.

通俗易懂的话来说就是你要求解当前位的next值,则看前一位与前一位next所在位字符是否相等,相等则当前位的next等于前一位next+1,如果不等就找前一位next的next与前一位比较,如果相等,当前位的next等于那个与前一位字符相等的字符所在位置+1,如果还是不相等,则以此类推,直到出现比较位为next[]=-1,那当前位的next就等于-1。
然而在算法求解的时候,我们应该这样去理解,求解下一位的next等于当前位与当前位的next比较。算法如下:

//next的求解
private static void getNext(int[] next, String str) {
        next[0]=-1;//初始化
        int k=-1;//记录当前位的next
        int j=0;//当前位下标
        while(j<str.length()-1){//求解完所有字符的next
            if(k==-1||str.charAt(j)==str.charAt(k)){//比较当前位与当前位next字符是否相等
                j++;
                k++;//当前位的next值+1作为下一位的next值
                next[j]=k;//求解出下一位的next值
            }else{
                k=next[k];//如果不相等则找当前位的next的next与当前位比较
            }
        }
    }

完整的KMP算法

在搞懂上面所有东西之后,那字符串匹配算法KMP就呼之欲出了。简言之,将主串与模式串匹配,相等的话彼此都+1,如果不等,主串j不回溯,将模式串的s=next[s]与主串j比较,相等彼此+1,以此类推,直到模式串完全被匹配,或者主串匹配到最末,结束。算法如下:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class KMP{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str1 = sc.nextLine();//主串
        while(sc.hasNext()){
            String str2 = sc.nextLine();//模式串
            int next[] = new int[str2.length()];
                getNext(next,str2);//求解next数组
                System.out.println("next数组"+java.util.Arrays.toString(next));
            List<Integer> pos = new ArrayList<>();//可能存在多个位置起始的字符串与模式串匹配,记录这些在主串中的位置
            ifMatch(str1,str2,next,pos);//字符串匹配过程
            System.out.println("匹配位置:"+pos);//输出所有匹配的位置
        }

    }

    private static void ifMatch(String str1, String str2, int[] next,List<Integer> pos) {
        int j=0;//主串初始位置
        int s=0;//匹配串初始位置
        while(j<str1.length()){
            if(s==-1||str1.charAt(j)==str2.charAt(s)){//比较字符是否相等
                j++;
                s++;
                if(s>=str2.length()){//模式串被完全匹配
                    pos.add(j-str2.length());
                    s=0;
                    j--;
                }
            }else{
                s=next[s];//不等,主串j不变,模式串s变
            }
        }

    }
      //next数组的求解
    private static void getNext(int[] next, String str) {
        next[0]=-1;
        int k=-1;
        int j=0;
        while(j<str.length()-1){
            if(k==-1||str.charAt(j)==str.charAt(k)){
                j++;
                k++;
                next[j]=k;
            }else{
                k=next[k];
            }
        }
    }
}

结语

或许其中有许多细节不是很明白,也没有公式的求证,但是很多时候我们应该删繁去杂,这样或许才能体会到其中的奥秘。当然每个人有每个人的理解,或许我的理解有误,欢迎大家拍砖!!!!

  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: AC自动机并没有直接使用KMP匹配算法,但是AC自动机的核心思想可以说是在KMP算法的基础上演化而来的。KMP算法的思想是通过预处理模式串,构建一个状态转移表,然后在匹配时利用状态转移表跳过匹配失败的前缀,从而实现快速匹配。AC自动机的思想也是类似的,在预处理模式串的基础上,构建一个有限状态机,然后在匹配文本串的过程中,根据当前字符和状态机的状态转移函数,快速地转移到下一个状态,从而实现快速匹配。因此,可以说AC自动机是KMP算法的一种扩展和升级,它在字符串匹配中具有更加高效和实用的特性。 ### 回答2: AC自动机并没有直接运用KMP匹配算法,但可以说AC自动机是在KMP算法的基础上进行了改进和优化。 KMP(Knuth-Morris-Pratt)算法是一种经典的模式匹配算法,用于在一个主串中查找一个模式串的出现位置。而AC自动机是用于多模式匹配的算法,可以在一个主串中同时查找多个模式串的出现位置。因此,AC自动机是在KMP算法的基础上进行了扩展和改进。 AC自动机的核心思想是构建一个有限状态自动机,在匹配过程中通过状态的转移来优化匹配效率。它利用了KMP算法中的失配函数的思想,但在实现细节和应用场景上有所不同。 AC自动机通过对所有模式串进行预处理和建立状态转移表,可以在O(n+m)的时间复杂度内完成多模式匹配,其中n是主串的长度,m是模式串的平均长度。而KMP算法仅能实现单模式匹配,时间复杂度为O(n+m)。这说明AC自动机在处理多模式匹配问题上的效率更高。 因此,尽管AC自动机没有直接运用KMP算法,但它是在KMP算法的基础上发展起来的一种更高效的多模式匹配算法。 ### 回答3: AC 自动机并没有直接运用 KMP 匹配算法,但是它的构造和搜索过程中使用了类似的思想。 KMP 算法是一种用于字符串匹配算法,通过预处理模式串来避免不必要的回退,以提高效率。它构建了一个匹配状态机,通过状态转移表来指导搜索过程。 AC 自动机是在多模式串匹配问题上的一种高效算法。它的核心思想是将所有模式串构建成一棵 Trie 树,并通过添加失败指针(failure link)来实现状态转移。这样,在搜索过程中,每次匹配失败时,不仅会跳转到 Trie 树中的下一个状态,还会根据失败指针跳转到一个更为合适的位置,避免了不必要的回退。 虽然 AC 自动机的构造和搜索过程中使用了和 KMP 算法相似的思想,但它们的应用场景和具体实现方法略有不同。AC 自动机主要用于在一个字符串中同时查找多个模式串的出现位置,而 KMP 算法则是以单个模式串作为输入,找到它在目标字符串中的匹配位置。 综上所述,AC 自动机并没有直接使用 KMP 算法,但在构造和搜索过程中使用了类似的思想,更加高效地解决了多模式串匹配问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值