C#利用KPM算法解决字符串匹配问题详解

 更新时间:2022年11月18日 11:15:33   作者:黑夜中的潜行者  
Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP算法”,常用于在一个文本串S内查找一个模式串P 的出现位置。本文将利用该算法解决字符串匹配问题,感兴趣的可以学习一下

什么是KPM算法

Knuth-Morris-Pratt 字符串查找算法,简称为 “KMP算法”,常用于在一个文本串S内查找一个模式串P 的出现位置,这个算法由Donald Knuth、Vaughan Pratt、James H. Morris三人于1977年联合发表,故取这3人的姓氏命名此算法。

KMP方法算法就利用之前判断过信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置,省去了大量的计算时间。

KPM的使用场景:模式串在文本串是否出现过,如果出现过,最早出现的位置

步骤

Ⅰ根据《最大长度表》部分匹配表(next)

对于P = p0 p1 ...pj-1 pj,寻找模式串P中长度最大且相等的前缀和后缀。如果存在p0 p1 ...pk-1 pk = pj- k pj-k+1...pj-1 pj,那么在包含pj的模式串中有最大长度为k+1的相同前缀后缀。

举个例子,如果给定的模式串为“abab”,那么它的各个子串的前缀后缀的公共元素的最大长度如下表格所示:

比如对于字符串aba来说,它有长度为1的相同前缀后缀a;而对于字符串abab来说,它有长度为2的相同前缀后缀ab(相同前缀后缀的长度为k + 1,k + 1 = 2)。

结论:最大前缀后缀元素长度所得到的数组就是我们所需要的 “部分匹配表”

寻找最长前缀后缀

如果给定的模式串是:“ABCDABD”,从左至右遍历整个模式串,其各个子串的前缀后缀分别如下表格所示:

Ⅱ 根据 部分匹配表 进行匹配

匹配失配,j = next [j],模式串向右移动的位数为:j - next[j]。换言之,当模式串的后缀pj-k pj-k+1, ..., pj-1跟文本串si-k si-k+1, ..., si-1匹配成功,但pj跟si匹配失败时,因为next[j] = k,相当于在不包含pj的模式串中有最大长度为k 的相同前缀后缀,即p0 p1 ...pk-1 = pj-k pj-k+1...pj-1,故令j = next[j],从而让模式串右移j - next[j]位,使得模式串的前缀p0 p1, ..., pk-1对应着文本串si-k si-k+1, ..., si-1,而后让pk跟si继续匹配。如下图所示:

KMP的next数组相当于告诉我们:

当模式串中的某个字符跟文本串中的某个字符匹配失配时,模式串下一步应该跳到哪个位置。如模式串中在j处的字符跟文本串在i处的字符匹配失配时,下一步用next [j]处的字符继续跟文本串i 处的字符匹配,相当于模式串向右移动j - next[j]位。

代码实现

字符串匹配问题:

有一个字符串 str1=““上海自来水来自海上””,和一个子串 str2=“自来水”。

现在要判断str1是否含有str2, 如果存在,就返回第一次出现的位置, 如果没有,则返回-1

static void Main(string[] args)
{
    string str1 = "上海自来水来自海上";
    string str2 = "自来水";
	
	// 得出 部分匹配表
    int[] next = KPMNext(str2);
    // 根据 得出的 部分匹配表的 next 数组进行匹配,
    int index = KPMSearch(str1, str2, next);
    
    Console.WriteLine(index);
}

/// <summary>
/// 获取一个字符串的部分匹配值表
/// </summary>
/// <param name="dest"></param>
/// <returns></returns>
static int[] KPMNext(string dest)
{
    // 初始化 数组大小
    int[] next = new int[dest.Length];
    // 字符串长度为1,部分匹配值就为0
    next[0] = 0;
    for (int i = 1, j = 0; i < dest.Length; i++)
    {
        while (j > 0 && dest[i] != dest[j])
        {
            j = next[j - 1];
        }
        if (dest[i] == dest[j])
        {
            j++;
        }
        next[i] = j;
    }
    return next;
}

/// <summary>
/// kmp搜索
/// </summary>
/// <param name="str1">源字符串</param>
/// <param name="str2">子字符串</param>
/// <param name="next">部分匹配表</param>
/// <returns></returns>
static int KPMSearch(string str1, string str2, int[] next)
{
    for (int i = 0, j = 0; i < str1.Length; i++)
    {
        while (j > 0 && str1[i] != str2[j])
        {
            j = next[j - 1];
        }

        if (str1[i] == str2[j])
        {
            j++;
        }

        if (j == str2.Length)
        {
            return i - j + 1;
        }
    }
    return -1;
}

到此这篇关于C#利用KPM算法解决字符串匹配问题详解的文章就介绍到这了,更多相关C# KPM字符串匹配内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#实现字符串进制转换方法汇总

    C#实现字符串进制转换方法汇总

    这篇文章主要介绍了C#实现字符串进制转换方法汇总,给大家罗列了十几种机制转换问题,感兴趣的朋友跟随小编一起看看吧
    2022-11-11
  • C#各种正则表达式验证方法整理大全

    C#各种正则表达式验证方法整理大全

    许多编程语言和工具都包含对正则表达式的支持,C#也不例外,这篇文章主要给大家介绍了关于C#各种正则表达式验证方法整理大全的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • 旧项目升级新版Unity2021导致Visual Studio无法使用的问题

    旧项目升级新版Unity2021导致Visual Studio无法使用的问题

    在项目开发过程中,不可避免的会升级开发工具。这次我在旧项目版本升级到新版Unity2021.2.x时,出现Visual Studio无法定位等问题,这里我给大家分享下解决方法,旧项目升级新版Unity2021导致Visual Studio无法使用的问题,需要的朋友可以参考下
    2021-12-12
  • 快速解决owin返回json字符串多带了双引号

    快速解决owin返回json字符串多带了双引号"多了重string转义字符串

    下面小编就为大家带来一篇快速解决owin返回json字符串多带了双引号"多了重string转义字符串。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • C# 实现Eval(字符串表达式)的三种方法

    C# 实现Eval(字符串表达式)的三种方法

    这篇文章主要介绍了C# 实现Eval(字符串表达式)的三种方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-04-04
  • C#机器入门学习之判断日报是否合格详解

    C#机器入门学习之判断日报是否合格详解

    这篇文章主要给大家介绍了关于C#机器入门学习之判断日报是否合格的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用c#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-03-03
  • C# 设计模式系列教程-观察者模式

    C# 设计模式系列教程-观察者模式

    将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
    2016-06-06
  • C#用正则表达式Regex.Matches 方法检查字符串中重复出现的词

    C#用正则表达式Regex.Matches 方法检查字符串中重复出现的词

    使用正则表达式用Regex类的Matches方法,可以检查字符串中重复出现的词,Regex.Matches方法在输入字符串中搜索正则表达式的所有匹配项并返回所有匹配,本文给大家分享C#正则表达式检查重复出现的词,感兴趣的朋友一起看看吧
    2024-02-02
  • C#调用Java代码的方法介绍

    C#调用Java代码的方法介绍

    这篇文章介绍了C#调用Java代码的方法,有需要的朋友可以参考一下
    2013-10-10
  • C# WPF Image控件的绑定方法

    C# WPF Image控件的绑定方法

    这篇文章主要介绍了C# WPF Image控件的绑定方法,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-03-03

最新评论