一文帮你搞懂 | 串的模式匹配-朴素匹配和KMP算法及优化

简介: 目录 朴素模式匹配算法KMP算法 求模式串的next数组总结:求模式串的next数组KMP算法优化

目录

 

朴素模式匹配算法

KMP算法

求模式串的next数组

总结:求模式串的next数组

KMP算法优化


朴素模式匹配算法

什么是模式匹配

串的模式匹配就是在子串中找到与模式串相同的子串,并返回其所在位置。

int idex(SString S,SString T){
  int k = 1;
  int i = k, j = 1;
  while(i <= S.length && j <= T.length)
  {
    if(S.ch[i] == T.ch[j])
    {
      i++;   
      j++;      //继续比较后面字符 
    }
    else{
      k++;    //检查下一个子串 
      i = k;
      j = 1;
    }
    if(j > T.length)
      return k;
    else
      return 0;
  } 
} 

为什么会出现return 0的情况,假设T为a  o  o,那么会出现 i 大于串的长度 ,而 j 没有超出串的长度,则跳出循环,匹配失败

image.png

朴素算法算法性能分析:

若模式串长度为m,主串长度为n,则

匹配成功的最好时间复杂度:O(m)

匹配失败的最好时间复杂度:O(n-m+1)=)(n-m)约等于O(n)

image.png

如果出现一下情况

image.png

最坏的时间复杂度是O(mn)

KMP算法

举个例子如下图,子串是google,主串是googlogooglegoolo

image.png

我们看看当我进行完前面的操作后,是否需要继续重i = 2, j = 1开始看起呢

看下面的例子

image.png

当我们发现j = 6的时候我们知道了 i  现在所指向一定不等于 e ,由于前面我们进行判断过,所以我们知道了i 不需要从 6 - 10开始匹配看,因为前面我们看过了我们不用回溯,况与 j = 1的时候比较就行。


移动之后,如果该字直接可以让i = 11的情符是  g 那么让那么i++ , j++ 。如果该字符不是 g  ,那么让·i++ ,j = 1 。4


如下图

image.png

第二种情况看下面例子

image.png

如果前面几个都进行了匹配但是突然发现当 j = 5的时候与i = 9的地方不匹配,那么回溯的时候,由于前面几个进行了匹配,i 不用回到 6,7的位置,当 i 到8的位置是可以的,因为我们只能确定 i= 9的位置不等于 I ,但是我们不确定是不是 o ,所以我们可以把 i 移到 8的位置,而   j  移到 8的位置,总的来说,就是如果  j = 5发生不匹配,那么  j 回到 2,而 i 到 8。

image.png

第三种情况看下面的例子

image.png

当 j = 4与i = 6,发生了不匹配 ,i = 4与 i = 5情况由于都是 o,一定不和  g 匹配,所以跳过,应该从i = 6 开始(但是实际上,i = 6 与 g 匹配过肯定不等于 g,这个情况先不考虑)


若 j = 4 的时候发生不匹配,应该让 j = 1,i = 6 。


第四种看下面的例子

image.png

如果 i = 5的时候,与 j = 3的时候不匹配,同样的只能确定 i = 5的位置不等于o,不能确定是否等于g ,所以如果 j = 3的时候,j 退到 j = 1,而 i 的位置不变

image.png

最后一种情况

image.png

如果 j  = 2的时候与 i = 4的时候不匹配,那么我们让 j = 1,i = 4。

汇总一下

image.png

我们把这些情况放到表格中去

这个表格叫next[7]

0 1 2 3 4 5 6
0 1 1 1 2 1

当 j = k ,且发现字符不匹配的时候,令  j = next[k] 来用

g o o g l e
1 2 3 4 5 6

 

代码

int Index_KMP(SString S,SString T,int next[]){
  int i = 1, j = 1;
  while(i <= S.length && j <= T.length){
    if(j == 0 || S.ch[i] == T.ch[i]){
      ++i;
      ++j;                 //继续比较后继字符
    }
    else{
      j = next[j];        //模式串向右移
    }
    if(j > T.length){      //匹配成功
      return  i - T.lenght;
    }
    else 
        return 0;
  }
}

注意:

1、j = 0 的情况是由于当  j = 1的时候  next [ j ]等于0,然后 j ++,变成 j = 1,i ++ 。

2、这里面 ++ j 与 ++ i 和 j ++ 与 i ++ 效果是一样的


求模式串的next数组

看下面的例子

image.png

当 j =  6匹配失败的时候,它的next[ 6 ] = 3

image.png

在看这个情况

image.png

当 i = 7匹配失败的时候 ,让 j 指向 j = 5 的位置,所以它的next[7] = 5

image.png

在看这个例子

image.png

当 j = 5 匹配失败的时候,把字符往右移一格

image.png

同样的也可以匹配到,我们让next [5] = 4 。虽然继续往后移主串与模式串仍能匹配,我们应该选择匹配长度最大的

继续看下一种情况

image.png

当  j = 5 不匹配的时候我们应该让 next [ j ] = 1

image.png

最后在看这个例子(为什么next[1] = 0)

image.png

当 j = 1,就匹配失败的时候  我们可以让   j  设置为 0,然后  j 与  i 同时 ++

即对于任何串都可以让 next [ 1 ] = 0

image.png

总结:求模式串的next数组

如果你没看懂上面的操作,没关系,只要知道如下操作就行

image.png

串的前缀:包含第一个字符,且不包含最后一个字符的子串.


串的后缀:包含最后一个字符,且不包含第一个字符的子串.


当第 j 个字符匹配失败,由前 1 --  j - 1个字符组成的串记为S,则:next [j] = S的最长相等的前后缀长度+ 1 。


特别 next[1] = 0;


下面通过一个列子来看


当模式串为 'a b a b a a'


序号  j 1 2 3 4 5 6

模式串 a b a b a a

next [j] 0 1 1 2 3 4

j 为1的时候无可置疑的选择next[ 1 ] =  0,


j 为2的时候ab相等前缀和后缀长度都为 0 ,next [ 2 ] = 1    (0+1)


j 为3的时候aba,前缀为a,后缀为b,不相等,next [ 3 ] = 1   ( 0+1)


j 为4的时候abab,前缀为a,后缀为a的时候最长相等子串,next [4] = 2    (1+1)


j 为5的时候ababa ,当前缀为ab,后缀为ab的时候为最长相等子串,next [5] = 3  ( 2+ 1)


j 为6的时候ababaa,当前缀为aba,后缀为aba的时候为最长相等子串,next [6]= 4  (3+ 1)

image.png

求模式串T的next数组

void get_next(SString T,int next[])
{
  int i = 1,j = 0;
  next[1] = 0;
  while(i < T.length){
    if(j ==0 || T.ch[i] == ch[j] )
    {
       ++i;
       ++j;
       next[i] = j; 
    } 
    else  
      j = next[j];
  }
}

上面的参考,其实也可以这样写

void get_next(String T)
{
  int i = 1,j = 0;
  next[1] = 0;
  while(i < T.length){
    if(j ==0 || T.ch[i] == ch[j] )
    {
       next[++i] = ++j; 
    } 
    else  
      j = next[j];
  }
}

KMP算法

int Index KMP(SSTring S,SString T){
  int i = 1,j = 1;
  int next[T.length+1];
  get_next(T,next);
  while(i<=S.length&&j<=T.length){
    if(j==0 || S.ch[i] == T.ch[j]){
      ++i;
      ++j;
    }
    else 
      j = next[j];
  } 
  if(j> T.length)
     return i - T.length;
  else 
     return 0;
} 

image.png

KMP算法优化

image.png

根据上面我们发现有这个情况,就是我们知道 i 指向 4 的位置 不等于g,但是我们仍有 next [ 4 ] = 1 ,继续比较了g,有点重复,所以我们可以作出优化,让next[ 4 ] = 0,j = 0 。 这样 j++,i++,让 j = 1 , 与 i = 5 进行比较了。

image.png

nextval数组的求法

先算出next数组
先令nextva[ 1 ] = 0
for (int j = 2; j <= T.lenght; j++){
           if(T.ch[next[ j ] == T.ch[j)
                       nextval[ j ] = nextval [next [ j ] ];
           else 
                       nextval [ j ] = next[ j ];
] }


相关文章
|
7天前
|
存储 关系型数据库 分布式数据库
PolarDB的PolarStore存储引擎以其高效的索引结构、优化的数据压缩算法、出色的事务处理能力著称
PolarDB的PolarStore存储引擎以其高效的索引结构、优化的数据压缩算法、出色的事务处理能力著称。本文深入解析PolarStore的内部机制及优化策略,包括合理调整索引、优化数据分布、控制事务规模等,旨在最大化其性能优势,提升数据存储与访问效率。
20 5
|
22天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
22天前
|
人工智能 算法 大数据
Linux内核中的调度算法演变:从O(1)到CFS的优化之旅###
本文深入探讨了Linux操作系统内核中进程调度算法的发展历程,聚焦于O(1)调度器向完全公平调度器(CFS)的转变。不同于传统摘要对研究背景、方法、结果和结论的概述,本文创新性地采用“技术演进时间线”的形式,简明扼要地勾勒出这一转变背后的关键技术里程碑,旨在为读者提供一个清晰的历史脉络,引领其深入了解Linux调度机制的革新之路。 ###
|
1天前
|
算法
基于大爆炸优化算法的PID控制器参数寻优matlab仿真
本研究基于大爆炸优化算法对PID控制器参数进行寻优,并通过Matlab仿真对比优化前后PID控制效果。使用MATLAB2022a实现核心程序,展示了算法迭代过程及最优PID参数的求解。大爆炸优化算法通过模拟宇宙大爆炸和大收缩过程,在搜索空间中迭代寻找全局最优解,特别适用于PID参数优化,提升控制系统性能。
|
2月前
|
人工智能 算法 数据安全/隐私保护
基于遗传优化的SVD水印嵌入提取算法matlab仿真
该算法基于遗传优化的SVD水印嵌入与提取技术,通过遗传算法优化水印嵌入参数,提高水印的鲁棒性和隐蔽性。在MATLAB2022a环境下测试,展示了优化前后的性能对比及不同干扰下的水印提取效果。核心程序实现了SVD分解、遗传算法流程及其参数优化,有效提升了水印技术的应用价值。
|
1月前
|
存储 缓存 算法
优化轮询算法以提高资源分配的效率
【10月更文挑战第13天】通过以上这些优化措施,可以在一定程度上提高轮询算法的资源分配效率,使其更好地适应不同的应用场景和需求。但需要注意的是,优化策略的选择和实施需要根据具体情况进行详细的分析和评估,以确保优化效果的最大化。
|
2月前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
2月前
|
存储 缓存 算法
前端算法:优化与实战技巧的深度探索
【10月更文挑战第21天】前端算法:优化与实战技巧的深度探索
22 1
|
2月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-LSTM网络的数据分类识别算法matlab仿真
本项目展示了基于贝叶斯优化(BO)的CNN-LSTM网络在数据分类中的应用。通过MATLAB 2022a实现,优化前后效果对比明显。核心代码附带中文注释和操作视频,涵盖BO、CNN、LSTM理论,特别是BO优化CNN-LSTM网络的batchsize和学习率,显著提升模型性能。
|
2月前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?