美文网首页
串匹配一:KMP算法

串匹配一:KMP算法

作者: wayyyy | 来源:发表于2017-10-20 01:00 被阅读0次
蛮力算法

蛮力串匹配算法是最直接和最直观方法。

蛮力算法.png
int BFmatch(const string& txt, const string& pat)
{
    size_t N = txt.size();    // 文本串长度
    size_t M = pat.size();    // 模式串长度
    
    int i, j;
    for (i = 0; i <= N-M; i++) {
        for (j = 0; j < M; j++) {
            if (txt[i+j] != pat[j])
                break;
        }
        if (j == M)
            return i;
    }

    return -1;   // 匹配失败返回-1 
}

理论上讲,蛮力算法至多迭代(n - m + 1)次,且每次迭代最多需要比较m次比对。所以总共需要(n - m + 1) * m次比对。因为m << n,所以渐进时间复杂度n*m。

KMP算法

KMP.png

现在D不匹配,按照蛮力匹配的方法,我们下一步要做的就是i = i+1来进行新一轮的匹配。

但是我们已经知道前面6个字符是"ABCDAB"(因为模式串前6个字符是匹配成功的)。KMP算法就是,设法利用这个已知信息,不要把搜索位置移回已经比较过的位置,继续把它向后移,这样就提高了效率

现在引入一个"部分匹配表"。

部分匹配表.png 利用部分匹配表匹配.png

现在介绍"部分匹配表"是怎么来的。

  • 字符串前缀和后缀

    如字符串"ababc",不考虑空字符
    所有的前缀有:a,ab,aba,abab
    所有的后缀有:c, bc,abc,babc

"部分匹配值"就是"前缀"和"后缀"集合中最长的共有元素的长度。以"ABCDABD"为例:

【1】"A"的前缀和后缀都为空。

【2】"AB"的前缀为[ A ],后缀为[ B ],共有元素长度为0。

【3】"ABC"的前缀为[ A, AB],后缀为[C, BC],共有元素长度为0。

【4】"ABCD"的前缀为[ A, AB, ABC ],后缀为[ D, CD, BCD ],共有元素长度为0。

【5】"ABCDA"的前缀为[ A, AB, ABC, ABCD ],后缀为[ BCDA, CDA, DA, A ],共有元素为"A",长度为1。

【6】"ABCDAB"的前缀为[ A, AB, ABC, ABCD, ABCDA ],后缀为[ B, AB, DAB, CDAB, BCDAB ],共有元素为"AB",长度为2。

【7】"ABCDABD"的前缀为[ A, AB, ABC, ABCD, ABCDA, ABCDAB ],后缀为[ D, BD, ABD, DABD, CDABD, BCDABD ],共有元素的长度为0。

所以部分匹配的实质就是:字符串头部和尾部会有相同的子字符串,比如"ABCDABD"之中首尾的"AB"。当匹配最后一个D失败时,第一个AB的匹配就可以移动到第2个AB重新开始。

那么如果模式串头部和尾部没有相同的子字符串,KMP算法就不起作用了呢?
答案是否的。当没有相同的子字符串时,如果存在部分匹配,那么我们就可以一次跳过已经部分匹配的字符,因为它们的不会像上一种情况有"利用价值"

KMP2.png
实现
/* 以下代码只为实现KMP核心思想,未考虑边界测试 */
std::vector<int> makeNext(const std::string& pat)
{
    int N = pat.size();
    std::vector<int> next(N, 0);
    
    for (int i = 1; i < N; i++) {
        for (int k = 1; k <= i; k++) {
            if (std::equal(pat.begin(), pat.begin()+k, pat.begin()+(i+1)-k))
                next[i] = k;
        }
    }   
    return next;
}
next.png
/* 以下代码只为实现KMP核心思想,未考虑边界测试 */
int KMPmatch(const std::string& txt, const std::string& pat)
{
    int n = txt.size();
    int m = pat.size();
    std::vector<int> next = makeNext(pat);
    bool flag = false;    // 记录部分匹配的标志位

    int i, j, skip;
    for (i = 0; i <= n-m; i += skip) {
        for (j = 0; j < m; j++) {
            if (txt[i+j] != pat[j]) {  
                skip = flag ? j-next[j-1] : 1; //根据这一轮是否有部分匹配,得到i要增加的值
                flag = false;
                break;
            } else {
                flag = true;      // 记录这一轮是否有部分匹配。
            }
        }
        if (j == m)
            return i;
    }

    return -1;
}

附:KMP算法与DFA

DFA.png

参考资料
[1] 《算法》4th
[2] http://www.cnblogs.com/c-cloud/p/3224788.html

相关文章

  • KMP算法文章合集

    字符串的查找:朴素查找算法和KMP算法 暴力匹配算法与KMP算法(串的匹配) 字符串查找算法BF和KMP 字符串匹...

  • 字符串匹配算法

    KMP算法 算法介绍 KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。...

  • KMP算法——寻找子串位置

    KMP算法——寻找子串位置 1、KMP算法简介: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J....

  • leetcode字符串匹配算法之KMP算法

    本篇介绍一种高效的字符串匹配算法——KMP算法。 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J....

  • 09--KMP

    [toc] KMP算法原理 KMP思想 假设字符串abcdefgab和模式串abcdex,进行匹配,当匹配到x位置...

  • 数据结构—KMP算法

    KMP算法是一种改进的字符串算法 KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速...

  • KMP算法(字符串匹配问题)

    一、是什么? 注意,是KMP算法,不是MMP哈,我没有骂人。KMP算法是用来做字符串匹配的,除了KMP算法分,还有...

  • 字符串匹配与KMP算法

    1.朴素字符串匹配算法 2.KMP算法 求前缀函数 实现KMP算法 3.测试代码

  • KMP字符串匹配算法

    KMP字符串匹配算法 先总结一下之前的几种字符串匹配算法 1 BF算法, 最简单的字符串匹配算法, 可以直接使用s...

  • 常见算法题之字符串

    1、KMP算法 参考:july大神的KMP博客细节不摆,该算法由暴力字符串来匹配,具体是由字符串匹配的暴力写法而来...

网友评论

      本文标题:串匹配一:KMP算法

      本文链接:https://www.haomeiwen.com/subject/qrgpuxtx.html