zoukankan      html  css  js  c++  java
  • 字符串匹配——KMP算法(leetcode 28)

    1.前言

      在一个字符串中寻找是否包含目标字符串,实现这个要求并不难,遍历文本的每个字符串,如果和目标字符串的第一个匹配,就把匹配的字符后移一位继续对比,直到不匹配,然后将文本的指针后移一位,继续对比即可。但是这样的暴力匹配最坏情况的时间复杂度为O(n*m),而KMP算法可以将其复杂度降低到O(n+m),减少重复对比次数。

    2.正文

      在学习KMP算法时,我翻阅了不少博客,但是五花八门的KMP介绍让我有点迷糊,有时候似乎看懂了,但是换一个例子我似乎不明白应该如何构造next数组,直到看了这个视频,对于KMP不懂的小伙伴们可以看看这个视频,视频大概在4-12分钟中讲解了如何构造利用next数组,并如何使用next数组减少重复对比。

      比起那些干涩的博客,这个视频提供了很好的例子说明如何在O(n)级数下构造next数组(利用已经求得的前面部分next来构造后面的部分),并且提供了几个例子生动的说明,next数组的作用:记录当前的后缀字串与前缀子串最大匹配长度。

      在这里不具体说明,有问题的小伙伴们可以去看看上面的视频,看完之后尝试自己动手写一个kmp算法。下面我会贴出看完这个视频后我自己实现的kmp。

    3.实现

      以下是KMP算法实现,主要包含kmp函数返回-1(未匹配)或匹配的第一个位置(下标从0开始),本人的next数组下标是从0开始到length-1,还包括一个getNext函数用来构造next数组,没有考虑text以及pattarn的一些异常输入,主要是为了更单纯的实现kmp算法。

     1     static int kmp(char[] text,char[] pattarn){
     2         int[] next = new int[pattarn.length];
     3         getnext(pattarn,next);
     4         int m = 0;//matchLength
     5         for(int i = 0;i<text.length;i++){
     6             while(m>0&& pattarn[m] != text[i]){
     7                 m = next[m-1];
     8             }
     9             if(text[i] == pattarn[m]){
    10                 m++;
    11                 if(m == pattarn.length){
    12                     return i - m+1;
    13                 }
    14             }
    15         }
    16         return -1;
    17     }
    18 
    19     private static void getnext(char[] pattarn, int[] next) {
    20         int q = 0;//q代表前一个字符前后缀能匹配的最大长度
    21         for(int i = 1;i<pattarn.length;i++){//next[0] = 0,因此从1开始
    22             while(q > 0 && pattarn[q] != pattarn[i]){//递归直到q为0(没有匹配的前缀)或者当前字符与q相等时(不断“递归”查前缀匹配的前一个位置q)
    23                 q = next[q-1];//如果不相等,如“acad”,i=3,q=1,则q变成next[q-1](q-1是不匹配的前一个位置)
    24             }
    25             if(pattarn[q] == pattarn[i]){
    26                 q++; 
    27             }
    28             next[i] = q;
    29         }
    30     }

      对于更完善的kmp以及入参的异常处理实现在下面贴出,也已经通过leetcode 28 Implement strStr()。其实就是对传入参数进行了一些意外值处理。

     1 class Solution {
     2     public int strStr(String haystack, String needle) {
     3         if (needle.length() == 0)
     4             return 0;
     5         if (haystack.length() < needle.length())
     6             return -1;
     7         
     8         char[] text = haystack.toCharArray();
     9         char[] pattarn = needle.toCharArray();
    10         int[] next = new int[pattarn.length];
    11         getnext(pattarn,next);
    12         int m = 0;//matchLength
    13         for(int i = 0;i<text.length;i++){
    14             while(m>0&& pattarn[m] != text[i]){
    15                 m = next[m-1];
    16             }
    17             if(text[i] == pattarn[m]){
    18                 m++;
    19                 if(m == pattarn.length){
    20                     return i - m+1;
    21                 }
    22             }
    23         }
    24         return -1;
    25     }
    26     
    27     private static void getnext(char[] pattarn, int[] next) {
    28         int q = 0;//q代表前一个字符前后缀能匹配的最大长度
    29         for(int i = 1;i<pattarn.length;i++){//next[0] = 0,因此从1开始
    30             while(q > 0 && pattarn[q] != pattarn[i]){//递归直到q为0(没有匹配的前缀)或者当前字符与q相等时(不断“递归”查前缀匹配的前一个位置q)
    31                 q = next[q-1];//如果不相等,如“acad”,i=3,q=1,则q变成next[q-1](q-1是不匹配的前一个位置)
    32             }
    33             if(pattarn[q] == pattarn[i]){
    34                 q++; 
    35             }
    36             next[i] = q;
    37         }
    38     }
    39 }

      在理解了KMP的流程后,赶紧动手实现一下吧。实现完还可以去leetcode测试一下哦!

  • 相关阅读:
    多任务GUI窗口系统(类window,有源码,支持汉字显示、顶层、非矩形和透明窗口)gicell源码
    怎样判断treeview当前节点为treeview显示出来的第一个节点和最后一个节点?
    用 dbgrid 或 dbgrideh 如何让所显示数据自动滚动
    vue路由懒加载及组件懒加载
    vue 中使用rem布局
    纯css实现移动端横向滑动列表&&overflow:atuo;隐藏滚动条
    分布式一致性算法Raft
    HDU_1753 大明A+B
    POJ——3630 Phone List
    HDU_2647 Reward (拓扑排序)
  • 原文地址:https://www.cnblogs.com/zzzdp/p/9416570.html
Copyright © 2011-2022 走看看