JMP算法实现及改进思想

一、KMP算法

参考视频

1、实现思想

  • 思想

KMP 算法的基本思想是利用已经匹配过的信息,尽量减少匹配的次数。其核心在于构建模式串的“部分匹配表”(Partial Match Table),也称为“next 数组”。该表记录了模式串中每个位置的最长前缀子串,使得该前缀子串同时也是该位置的后缀子串的长度。这个表的构建过程是 KMP 算法的关键步骤之一。

2、匹配动画

  • KMP算法匹配动画

KMP算法实现动画

  • 数组动画

KMP算法数组实现动画

  • 代码实现

int KMPIndex(SqString s, SqString t) {  // 定义KMP算法函数,返回匹配的起始位置索引
     int next[MaxSize], i = 0, j = 0;  // 定义next数组,以及两个索引i和j
 ​
     GetNext(t, next);  // 调用GetNext函数获取模式串t的next数组
 ​
     // 当主串s和模式串t均未遍历完时进行匹配
     while (i < s.length && j < t.length) {
         // 当前字符匹配或已到达模式串开头时,继续匹配下一个字符
         if (j == -1 || s.data[i] == t.data[j]) {
             i++;  // 主串指针后移
             j++;  // 模式串指针后移
         } else {
             j = next[j];  // 模式串指针回溯到next[j]位置
         }
     }
 ​
     // 若模式串已完全匹配,则返回匹配的起始位置索引
     if (j >= t.length)
         return (i - t.length);
     else
         return (-1);  // 否则返回匹配失败标志
 }
 ​

3、改进Kmp算法

大家来看一个例子: 主串s=“aaaaabaaaaac” 子串t=“aaaaac”

这个例子中当‘b’与‘c’不匹配时应该‘b’与’c’前一位的‘a’比,这显然是不匹配的。'c’前的’a’回溯后的字符依然是‘a’。 我们知道没有必要再将‘b’与‘a’比对了,因为回溯后的字符和原字符是相同的,原字符不匹配,回溯后的字符自然不可能匹配。但是KMP算法中依然会将‘b’与回溯到的‘a’进行比对。这就是我们可以改进的地方了。我们改进后的next数组命名为:nextval数组。KMP算法的改进可以简述为: 如果a位字符与它next值指向的b位字符相等,则该a位的nextval就指向b位的nextval值,如果不等,则该a位的nextval值就是它自己a位的next值。 这应该是最浅显的解释了。如字符串"ababaaab"的next数组以及nextval数组分别为:

下标01234567
子串ababaaab
next-10012311
nextval-10-10-1310
  • 代码实现

 
int KMPIndex1(SqString s, SqString t)    
 // 修正的KMP算法
 // 函数名:KMPIndex1
 // 参数:s为主串,t为模式串
 {
     int nextval[MaxSize], i = 0, j = 0;
     GetNextval(t, nextval); // 调用GetNextval函数获取模式串的nextval数组
     while (i < s.length && j < t.length){
         if (j == -1 || s.data[i] == t.data[j]){ 
             i++; j++;   // 主串和模式串匹配,继续向后移动
         }else{
             j = nextval[j]; // 主串和模式串不匹配,根据nextval数组移动模式串指针
         }
     }
     if (j >= t.length)  
         return (i - t.length); // 返回主串中匹配到模式串的起始位置
     else
         return (-1); // 主串中未匹配到模式串,返回-1
 }

  • 16
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是KMP算法的汇编实现,代码中已经加注释: ``` ; KMP算法汇编实现 ; 输入参数:pattern-模式串,pattern_len-模式串长度,text-文本串,text_len-文本串长度 ; 输出参数:如果匹配成功,返回匹配成功的位置;否则,返回-1。 ; 定义常量 %define PATTERN_LEN dword [ebp + 8] ; 模式串长度 %define PATTERN dword [ebp + 12] ; 模式串 %define TEXT_LEN dword [ebp + 16] ; 文本串长度 %define TEXT dword [ebp + 20] ; 文本串 section .text global kmp kmp: ; 函数开头,保存当前栈帧 push ebp mov ebp, esp ; 检查参数是否合法 cmp PATTERN_LEN, 0 jle .error cmp TEXT_LEN, 0 jle .error ; 分配内存,用于存储next数组 push PATTERN_LEN call malloc mov ebx, eax ; 将返回的地址存储到ebx寄存器中,用于之后的释放内存操作 add esp, 4 ; 因为调用malloc时参数传递了一个dword,所以这里需要将栈指针调整回来 ; 初始化next数组 mov ecx, PATTERN_LEN mov eax, 0 mov edi, ebx ; edi寄存器存储next数组的起始地址 rep stosd ; 将next数组中的所有元素设置为0 ; 计算next数组 mov ecx, PATTERN_LEN mov esi, 1 ; esi寄存器用于遍历模式串中的字符 mov edx, 0 ; edx寄存器用于存储当前字符之前最长的相等前缀后缀长度 mov dword [ebx], 0 ; next[0] = 0 .loop: cmp esi, ecx je .end ; 如果已经遍历完了整个模式串,跳转到结束标签 cmp edx, 0 jne .not_first ; 如果当前字符之前有相等前缀后缀,跳转到not_first标签 .first: mov eax, esi ; 如果当前字符之前没有相等前缀后缀,将next[esi]赋值为esi mov dword [ebx + eax * 4], eax ; next[esi] = esi inc esi ; 继续遍历下一个字符 jmp .loop .not_first: mov edi, dword [ebx + edx * 4] ; edi寄存器存储当前字符之前最长的相等前缀后缀的长度 cmp byte [PATTERN + esi - 1], byte [PATTERN + edi] ; 比较当前字符和相等前缀后缀的下一个字符是否相等 je .match ; 如果相等,跳转到match标签 cmp edx, 1 jne .not_first ; 如果当前字符之前不止一个相等前缀后缀,跳转到not_first标签 mov dword [ebx + esi * 4], 0 ; 如果当前字符之前只有一个相等前缀后缀,将next[esi]赋值为0 inc esi ; 继续遍历下一个字符 jmp .loop .match: inc edx ; 更新当前字符之前最长的相等前缀后缀的长度 mov dword [ebx + esi * 4], edx ; 将next[esi]赋值为当前字符之前最长的相等前缀后缀的长度 inc esi ; 继续遍历下一个字符 jmp .loop .end: ; KMP算法匹配过程 mov esi, 0 ; esi寄存器用于遍历文本串中的字符 mov edx, 0 ; edx寄存器用于存储当前匹配的模式串中的字符数 mov eax, -1 ; 如果匹配失败,将返回值设为-1 .loop2: cmp esi, TEXT_LEN je .end2 ; 如果已经遍历完了整个文本串,跳转到结束标签 cmp edx, 0 jne .not_first2 ; 如果当前已经匹配了一部分字符,跳转到not_first2标签 .first2: cmp byte [PATTERN], byte [TEXT + esi] ; 比较第一个字符是否匹配 jne .mismatch ; 如果不匹配,跳转到mismatch标签 mov edx, 1 ; 如果匹配,将当前匹配的字符数设为1 inc esi ; 继续遍历下一个字符 jmp .loop2 .not_first2: mov edi, dword [ebx + edx * 4] ; edi寄存器存储当前匹配的字符之前最长的相等前缀后缀的长度 cmp byte [PATTERN + edi], byte [TEXT + esi] ; 比较当前字符和相等前缀后缀的下一个字符是否相等 je .match2 ; 如果相等,跳转到match2标签 mov edx, dword [ebx + edi * 4] ; 如果不相等,将当前匹配的字符数设为当前匹配的字符之前最长的相等前缀后缀的长度 cmp edx, 0 je .first2 ; 如果相等前缀后缀的长度为0,重新从第一个字符开始匹配 jmp .not_first2 ; 继续匹配下一个字符 .match2: inc edx ; 如果匹配,将当前匹配的字符数加1 cmp edx, PATTERN_LEN ; 如果当前匹配的字符数等于模式串长度,说明匹配成功 je .success inc esi ; 继续遍历下一个字符 jmp .loop2 .mismatch: mov edx, dword [ebx] ; 如果不匹配,将当前匹配的字符数设为0 cmp edx, 0 je .first2 ; 如果相等前缀后缀的长度为0,重新从第一个字符开始匹配 jmp .not_first2 ; 继续匹配下一个字符 .success: mov eax, esi ; 如果匹配成功,将返回值设为匹配成功的位置 .end2: ; 释放内存,返回结果 push ebx call free pop ebx mov esp, ebp pop ebp ret .error: ; 如果参数不合法,直接返回-1 mov eax, -1 jmp .end2 ``` 以上是KMP算法的汇编实现,希望能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值