(超简单、超易懂、超详细)算法精讲(二十六): 克努斯-莫里斯-普拉特算法

如果你也喜欢C#开发或者.NET开发,可以关注我,我会一直更新相关内容,并且会是超级详细的教程,只要你有耐心,基本上不会有什么问题,如果有不懂的,也可以私信我加我联系方式,我将毫无保留的将我的经验和技术分享给你,不为其他,只为有更多的人进度代码的世界,而进入代码的世界,最快捷和最容易的就是C#.NET,准备好了,就随我加入代码的世界吧!
一、算法简介

        克努斯-莫里斯-普拉特(Knuth-Morris-Pratt)算法(以下简称KMP算法)是一种用于字符串匹配的算法。它的目标是在一个主字符串中查找一个模式字符串的出现位置,时间复杂度为O(n+m),其中n是主字符串的长度,m是模式字符串的长度。

        该算法的核心思想是利用已经匹配的部分信息,将模式字符串在主字符串中进行滑动,从而减少不必要的比较次数。

算法的步骤如下:

  1. 使用一个辅助数组next[],用来保存模式字符串中的每个位置对应的最长公共前缀后缀长度。
  2. 初始化两个指针i和j,分别指向主字符串和模式字符串的开头。
  3. 如果主字符串和模式字符串的当前字符匹配,则将两个指针都向后移动一位。
  4. 如果模式字符串的当前位置是0,则说明已经匹配到了模式字符串的开头,将两个指针都向后移动一位。
  5. 如果模式字符串的当前位置不是0,则将指针j移动到next[j-1],即向左移动到最长公共前缀后缀的末尾。
  6. 如果某一次比较中指针j移动到了模式字符串的末尾,则说明找到了一个匹配,将匹配位置保存,并将指针j移动到next[j-1],继续匹配下一个位置。
  7. 如果最终指针j移动到了模式字符串的末尾,则说明找到了所有匹配位置。

        KMP算法的优势在于,它避免了不必要的比较,减少了时间复杂度。它在字符串匹配问题中被广泛应用,例如在文本编辑器中查找和替换字符串,以及在搜索引擎中进行关键字搜索。

二、为什么要学习克努斯-莫里斯-普拉特算法:

        2.1 提高字符串匹配效率:

        KMP算法能够在O(n+m)的时间复杂度内实现字符串匹配,其中n为待匹配串的长度,m为模式串的长度。相比于暴力匹配算法的O(n*m)的时间复杂度,KMP算法大大提高了匹配效率。

        2.2 解决字符串匹配问题:

        字符串匹配是计算机领域中常见的一个问题,涉及到文本搜索、数据压缩、模式识别等多个领域。KMP算法提供了一种高效、优化的解决方案,能够快速地找到字符串中的特定模式。

        2.3 理解字符串匹配原理:

        通过学习KMP算法,能够深入理解字符串匹配的原理和实现细节。KMP算法基于字符串的前缀与后缀的概念,通过构建部分匹配表(Partial Match Table)来实现字符串的快速匹配。了解这些概念和实现原理,对于理解其他字符串匹配算法也会有帮助。

        2.4 实际应用:

        KMP算法被广泛应用于各种字符串匹配场景,例如文本编辑器的查找功能、搜索引擎的关键字搜索、字符串匹配算法的优化等。掌握KMP算法能够加深对这些应用的理解,并且能够在实际问题中快速解决字符串匹配的需求。

三、克努斯-莫里斯-普拉特算法在项目中有哪些实际应用:

        3.1 文本编辑器:

        在文本编辑器中,常常需要实现搜索功能,例如查找某个单词或者查找并替换某个字符串。KMP算法可以高效地实现这些功能,提高搜索速度。

        3.2 数据库查询:

        在数据库查询中,有时需要进行模糊匹配或者模式匹配,KMP算法可以用来实现快速的字符串匹配。

        3.3 DNA序列匹配:

        在生物信息学中,常常需要进行DNA序列的匹配和比对。KMP算法可以帮助快速找到DNA序列中的特定模式。

        3.4 字符串处理:

        在字符串处理的各种场景中,KMP算法可以帮助实现快速的字符串匹配、查找和替换等操作。

        3.5 编译器:

        在编译器的词法分析和语法分析中,需要进行字符串匹配和搜索。KMP算法可以帮助实现高效的词法分析和语法分析。

四、克努斯-莫里斯-普拉特算法的实现与讲解:

        4.1 克努斯-莫里斯-普拉特算法的实现

using System;

public class KMPAlgorithm
{
    // 构建部分匹配表
    public int[] BuildPartialMatchTable(string pattern)
    {
        int[] table = new int[pattern.Length];
        int i = 0, j = 1;

        while (j < pattern.Length)
        {
            if (pattern[i] == pattern[j])
            {
                // 如果当前字符匹配,则将部分匹配值加一,并同时移动i和j
                table[j] = i + 1;
                i++;
                j++;
            }
            else
            {
                if (i != 0)
                {
                    // 如果当前字符不匹配,且i不等于0,则回退到上一个匹配位置
                    i = table[i - 1];
                }
                else
                {
                    // 如果当前字符不匹配,且i等于0,则将部分匹配值置为0,并向后移动j
                    table[j] = 0;
                    j++;
                }
            }
        }

        return table;
    }

    // 使用KMP算法进行字符串匹配
    public int KMPSearch(string text, string pattern)
    {
        int[] table = BuildPartialMatchTable(pattern);
        int i = 0, j = 0;

        while (i < text.Length)
        {
            if (pattern[j] == text[i])
            {
                // 如果当前字符匹配,则同时移动i和j
                i++;
                j++;
            }
            else
            {
                if (j != 0)
                {
                    // 如果当前字符不匹配,且j不等于0,则回退到上一个匹配位置
                    j = table[j - 1];
                }
                else
                {
                    // 如果当前字符不匹配,且j等于0,则向后移动i
                    i++;
                }
            }

            if (j == pattern.Length)
            {
                // 找到匹配字符串,返回起始位置
                return i - j;
            }
        }

        return -1; // 没有找到匹配字符串
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        string text = "ABABDABACDABABCABAB";
        string pattern = "ABABCABAB";
        KMPAlgorithm kmpAlgorithm = new KMPAlgorithm();
        int startIndex = kmpAlgorithm.KMPSearch(text, pattern);

        if (startIndex != -1)
        {
            Console.WriteLine("匹配字符串起始位置: " + startIndex);
        }
        else
        {
            Console.WriteLine("未找到匹配字符串");
        }
    }
}

        4.2 克努斯-莫里斯-普拉特算法的讲解

        上述代码首先定义了一个KMPAlgorithm类,其中包含了两个方法:BuildPartialMatchTableKMPSearchBuildPartialMatchTable方法用于构建部分匹配表,它通过遍历模式字符串中的字符,生成一个与模式字符串等长的部分匹配表。KMPSearch方法则是使用KMP算法进行字符串匹配,它首先调用BuildPartialMatchTable方法构建部分匹配表,然后通过比较文本字符串和模式字符串的字符进行匹配。

        在BuildPartialMatchTable方法中,我们使用两个指针ij来遍历模式字符串,同时构建部分匹配表。当当前字符匹配时,我们将部分匹配值加一,并同时移动ij。当当前字符不匹配时,如果i不等于0,则回退到上一个匹配位置,否则将部分匹配值置为0,并向后移动j。最后返回构建好的部分匹配表。

        在KMPSearch方法中,我们同样使用两个指针ij来遍历文本字符串和模式字符串,并通过比较字符来进行匹配。当当前字符匹配时,同时移动ij。当当前字符不匹配时,如果j不等于0,则回退到上一个匹配位置,否则只向后移动i。当j等于模式字符串的长度时,表示找到了匹配字符串,返回起始位置i - j。如果没有找到匹配字符串,返回-1。

        最后,在Main方法中我们使用了一个示例进行测试。我们在文本字符串中查找模式字符串,如果找到了匹配字符串,则输出起始位置,否则输出未找到匹配字符串。

五、克努斯-莫里斯-普拉特算法需要注意的是:

        5.1 构建部分匹配表时,需要注意表中每个位置的值的含义。表中的每个位置 i 的值表示模式串的前 i 个字符中,有多少个字符是前缀同时也是后缀。这个值会在匹配失败时,用于确定下一次匹配的位置。

        5.2 在构建部分匹配表时,要注意处理重复的前缀。如果模式串中出现两个相同的前缀,那么它们对应的值应该相等。这可以通过动态规划的思想来实现。

        5.3 在实现算法的代码中,需要使用两个指针来表示文本串和模式串的当前位置。其中,文本串的指针每次都向后移动一位,而模式串的指针则根据部分匹配表的值来进行移动。这个移动规则是算法的核心,需要仔细理解和实现。

        5.4 在匹配过程中,如果模式串的指针移动到了模式串的末尾,说明找到了一个匹配。此时,可以根据需要进行相关操作,例如记录匹配的位置或者统计匹配的次数。

        5.5 算法中的时间复杂度为 O(n+m),其中 n 是文本串的长度,m 是模式串的长度。这是因为算法需要遍历一遍文本串,并且在匹配过程中,模式串的指针不会后退,因此总共移动的距离不会超过模式串的长度。

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值