KMP算法

103 阅读1分钟

1.KMP算法

KMP算法是一种更高效的字符串匹配算法,它可以在一个文本串中查找一个模式串出现的位置,名字来源于3位算法发明人的名字首字母。与暴力匹配算法相比,KMP具有更快的匹配速度。

2. 字符串匹配问题

问题:[leetcoe](28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode))

设主串s='ababcabcacbab',模式串t='abcac',返回s中t字符串的第一次出现位置,未匹配返回-1。

暴力解法

字符串匹配问题,暴力解法相对比较简单,解法也比较容易想到,单纯的将2个字符串挨个比对即可。

        // 暴力匹配字符串
       int strStr(string haystack, string needle) {
           int m = haystack.size(),n = needle.size();
           // i:主串索引
           // j:模式串索引
           int i = 0,j = 0;
           while(i < m && j < n)
           {
               // 当前索引位置字符相同,就继续比较下一个
               if(haystack[i] == needle[j])
               {
                   i++;
                   j++;
               }
               else
               {
                   // 不匹配了,之前比对过的需要回退,
                   // i回退到起始比对位置的下一个位置,j回退到开头
                   i = i-j+1;
                   j = 0;
               }
                // j == n代表匹配结束,模式串都比对了一遍且都相等,说明找到了
               if(j == n)
               {
                   // 匹配成功了,返回位置
                    return i-j;
               }
           }
           return -1;
       }

这是最容易想到的一类解法,能够通过leetcode。但是这种实现方式有2个点可以优化

  1. 如果模式串比主串还长,显然不用比了,直接返回-1即可
  2. 主串和模式串比较时,i不用索引到最后一个元素,实际上比较到m-n个元素时就够了,再后面主串剩余的字符长度比模式串小,不可能相等的

这里再优化一下更优的实现方式

    int strStr(string haystack, string needle) 
    { 
        int n = haystack.length(), m = needle.length(); 
        // i遍历到[0,n-m]区间就够了,且优化了m > n的情况
        for (int i = 0; i <= n - m; ++i) 
        { 
            // 由j逐个往前比对,不匹配则由i从i+1位置继续
            int j; 
            for (j = 0; j < m; ++j) 
            { 
                if (haystack[i + j] != needle[j]) { break; } 
            } 
            if (j == m) { return i; } 
        } 
            return -1; 
    }

2.KMP算法思路和实现

算法思路 :核心思想是当模式串匹配过程中,某个索引匹配失败了,避免从头开始匹配,减少回退次数。能不能避免回退由模式串本身决定,跟主串无关。KMP算法借助Next数组在某个位置未匹配上时,取得下一个应该比对的索引位置。

1.首先看下KMP算法辅助数组Next数组的含义和怎么计算

vector<int> getNext(string pattern) {
    int n = pattern.size();
    // 初始化next数组,全部为0
    vector<int> next(n, 0); 
    // i从1开始遍历,j从0开始
    for (int i = 1, j = 0; i < n; i++) { 
    // 如果不相等,就回退j
        while (j > 0 && pattern[i] != pattern[j]) { 
            j = next[j - 1];
        }
        // 如果相等,就将next[i]赋值为j+1
        if (pattern[i] == pattern[j]) { 
            j++;
            next[i] = j;
        }
    }
    return next;
}
  1. kmp算法完整实现
#include <iostream>
#include <vector>
using namespace std;


    int strStr(string haystack, string needle) {
        int m = haystack.size(),n = needle.size();
        vector<int> next = getNext(needle);
        for(int i = 0,j = 0; i < m ;i++)
        {
            // 不相等,j回退。
            // 1. j == 0 时,停止回退
            // 2. 回退过程中,回退位置和i当前位置相等了,不用继续回退
           while(j > 0 && haystack[i] != needle[j])
           {
               j = next[j-1];
           }
            // 相等,j继续++就好;i++由外层循环控制
           if(haystack[i] == needle[j])
           {
               j++;
           }

           if(j == n){
               return i-n+1;
           }
        }
        return -1;
    }

int main() {
    string text = "ababcabcacbab", pattern = "abcac";
    kmp(text, pattern);
    return 0;
}

kmp算法讲解视频推荐