字符串匹配——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测试一下哦!

posted @ 2018-08-03 22:18  zhangdapao  阅读(4575)  评论(0编辑  收藏  举报