字符串匹配算法---KMP算法

本文深入讲解了KMP算法的核心概念及其实现步骤,并通过实例演示了如何求解next数组及进行字符串匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串匹配问题是计算机编程中一个很常见的问题。最近在学习小甲鱼的数据结构与算法,正好学到字符串匹配的经典算法—KMP算法。在小甲鱼图文并茂的解说下,加上动手实践,基本上弄清了算法的关键所在。该算法的核心在于求取待匹配串的 next 数组,为了更好的解释算法过程。定义一个结构体String,它有两个成员,一个字符型指针变量 str 用来存储字符串,一个整形变量 length 用来存储字符串的长度。

1.next 数组的求解过程

以一个字符串 T="ababaaaba" 为例来进行说明:
第1步:i=1,j=0 next[1] = 0;
1

第2步:i=2,j=1 next[2] = 1;
2

第3步:i=3,j=1 ,由于 T[2]T[1],则j=next[j]=next[1]=0,当j=0时,j=1,next[3]=j=1;
3

第4步:由于 T[3]=T[1],则 i=i+1=4,j=2,next[4]=j=j+1=2
4

第5步:由于T[4]=T[2],则 i=i+1=5,j=j+1=3,next[5]=j=3
5

第6步:由于T[5]=T[3],则 i=i+1=6,j=4,next[6]=j=4
6

第7步:由于T[6]T[4],则j=next[4]=2,又由于T[6]T[2],则j=next[2]=1,此时 T[6] = T[1} ,则i=i+1=7,j=j+1=2,next[7]=j=2
7

第8步:由于T[7]T[2],则j=next[j]=next[2]=1,此时T[7]=T[1],则 i=i+1=8,j=j+1=2,next[8]=j=2
8

第9步:由于T[8] = T[2},则i=i+1=9,j=j+1=3,next[9]=j=3,到达串尾,整个过程结束。
9

小结: 我个人觉得记住这个过程的一个诀窍是看当前点之前的串,向左和向右有几个重复的字符,对应的 next 就等于相等的字符数加 1 .例如i=9时,看前面的8个字符,向左和向右只有ab=ab,所以next[9]=2+1=3

2.KMP匹配的过程

待匹配的串的next数组求出之后,匹配就比较容易了。从指定点开始匹配,当遇到不匹配的情况的时候,利用next数组的值改变匹配串匹配的起始位置,直到匹配结束或者到达串尾。

3.C++代码实现
#include <iostream>

using namespace std;

typedef struct
{
    int length;  // 用来记录字符串的长度
    char *str;   // 用来存储字符串
}String;

// 求出待匹配串的next数组,用来指导后续的匹配
void get_next(String T, int *next)
{
    int i = 1;
    int j = 0;
    next[1] = 0;
    while( i < T.length)
    {
        if( 0 == j || T.str[i] == T.str[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
        {
            j = next[j];
        }
    }
}

// 在字符串 S 中匹配字符串 T
// 匹配成功,返回匹配点的位置
// 匹配失败,返回0
int KMP(String S, String T, int pos)
{
    int i = pos;
    int j = 1;
    int next[255];

    get_next(T, next);  // 获得最高指导方针,next数组

    while( i <= S.length && j <= T.length )
    {
        if(0 == j || S.str[i] == T.str[j] )  // 必须要加上 j==0 ,否则后面会出现next 引用越界的的情况
        {
            i++;
            j++;
        }
        else
        {
            j = next[j];
        }

    }
    if( j > T.length )  // 如果 j 的值大于匹配串的长度,说明匹配成功。返回匹配点的位置
    {
        return i - T.length;
    }
    else
    {
        return 0;
    }

}

int main()
{
    String S,T;
    char *str = " ababaaaba";
    S.str = str;
    S.length = strlen(str)-1; // 排除串前的空格符

    char *str1 = " baa";
    T.str = str1;
    T.length = strlen(str1)-1;

    int k;
    k = KMP(S, T, 0);

    if( 0 == k )
    {
        cout << "匹配失败!" << endl;
    }
    else
    {
        cout << "匹配成功! 匹配点为: " << k << endl;
    }

/////////////////////////////////////
// 显示 S 串的 next 数组
    int next[255];
    get_next(S, next);
    for(int i=1; i <= S.length; i++ )
    {
        cout << next[i] << " ";
    }
    cout << endl;

    system("pause");

    return 0;
}

运行结果为:
结果

道01数据结构算法绪论. mp402_谈谈算法. mp4 西03_时间复杂度和空间复杂度.mp404_时间复杂度和空间复杂度2.mp405_时间复杂度和空间复杂度3.mp4险06线性表. mp407_线性表2. mp408_线性表3. mp4品09_ 线性表4. mp410_线性表5. mp411_线性表6. mp4@12_线性表7. mp413_线性表8. mp4西14. 线性表9. mp415_线性表10. mp4 16_单链表小结:腾讯面试题. mp4品17_ 线性表12. mp418_约瑟夫问题. mp4西19_ 线性表14. mp4 20_魔术师发牌问题. mp421线性表16. mp4逾22_ 线性表17. mp423_栈和队列. mp424_栈和队列2. mp4面25_ 进制转换. mp4面26_ 栈和队列4. mp427_逆波兰计算器mp4 28_中缀表达式转换为后缀表达式01. mp4逾29_ 中缀表达式转换为后缀表达式02. mp430_栈和队列7. mp431_栈和队列8. mp4 西32递归和分治思想.mp433_递归和分治思想2. mp434_汉诺塔. mp4 35_八皇后问题. mp4 四36_字符串.mp4 二37_ KMP算法. mp4 四71斐波那契查找(黄金分割法查找).38_ KMP算法2. mp4 立39_ KMP算法之NEXT数组代码原理分析. mp4二40_ KMP算法之实现及优化. mp4二41树. mp4 四42_树的存储结构. mp443_树的存储结构2. mp4四44_二艾树. mp445_二叉树2. mp4 46_二又树的存数结构. mp447_二又树的遍历. mp4 48_二丈树的建立和遍历算法. mp4四49_线索二叉树. mp4 50_线索二又树代码实现. mp4 画51_树、森林及二又树的相互转换. mp452_赫夫曼树. mp453_赫夫曼编码. mp4 四54_赫夫曼编码C语言实现. mp4口55_图. mp4 逾56_图的定义与术语2. mp457_图的存储结构. mp4 58_图的存储结构(邻接表) . mp4 59_图的存储结构(十字链表、邻接多重表、边集数组) . mp4四93堆排序的代码实现mp460_图的遍历(深度优先遍历) . mp4 品94归并排序. mp4 四61_马踏棋盘算法(骑士周游问题) . mp4 95归并排序(迭代实现) . mp4品62_图的遍历(广度优先遍历) . mp4 國96快速排序.mp4 63_最小生成树(普里姆算法) . mp4 二97快速排序的优化mp464_最小生成树( 克鲁斯卡尔算法) . mp4 立98总结回顾.mp4画65_最短路径(迪杰斯特拉算法).mp466_最短路径( 弗洛伊德算法) . mp4口67拓扑排序. mp4二68关键路径.mp4口69_查找算法. mp4 画69关键路径(代码讲解).mp4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值