一文帮你搞懂 | 串的模式匹配-朴素匹配和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 ];
] }


相关文章
|
23天前
|
机器学习/深度学习 算法 数据挖掘
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
没发论文的注意啦!重磅更新!GWO-BP-AdaBoost预测!灰狼优化、人工神经网络与AdaBoost集成学习算法预测研究(Matlab代码实现)
|
23天前
|
机器学习/深度学习 数据采集 传感器
具有多种最大功率点跟踪(MPPT)方法的光伏发电系统(P&O-增量法-人工神经网络-模糊逻辑控制-粒子群优化)之使用粒子群算法的最大功率点追踪(MPPT)(Simulink仿真实现)
具有多种最大功率点跟踪(MPPT)方法的光伏发电系统(P&O-增量法-人工神经网络-模糊逻辑控制-粒子群优化)之使用粒子群算法的最大功率点追踪(MPPT)(Simulink仿真实现)
|
23天前
|
机器学习/深度学习 算法 安全
计及需求响应的粒子群算法求解风能、光伏、柴油机、储能容量优化配置(Matlab代码实现)
计及需求响应的粒子群算法求解风能、光伏、柴油机、储能容量优化配置(Matlab代码实现)
|
1月前
|
算法 数据安全/隐私保护
基于PSO粒子群优化算法的256QAM星座图的最优概率整形matlab仿真,对比PSO优化前后整形星座图和误码率
本项目基于MATLAB 2022a仿真256QAM系统,采用概率星座整形(PCS)技术优化星座点分布,结合粒子群优化(PSO)算法搜索最优整形因子v,降低误码率,提升传输性能。核心程序包含完整优化流程。
61 0
|
20天前
|
机器学习/深度学习 算法 数据可视化
近端策略优化算法PPO的核心概念和PyTorch实现详解
本文深入解析了近端策略优化(PPO)算法的核心原理,并基于PyTorch框架实现了完整的强化学习训练流程。通过Lunar Lander环境展示了算法的全过程,涵盖环境交互、优势函数计算、策略更新等关键模块。内容理论与实践结合,适合希望掌握PPO算法及其实现的读者。
204 2
近端策略优化算法PPO的核心概念和PyTorch实现详解
|
17天前
|
机器学习/深度学习 算法 新能源
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)
【优化调度】基于matlab粒子群算法求解水火电经济调度优化问题研究(Matlab代码实现)
|
20天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化的XGBoost时间序列预测算法matlab仿真
本程序基于Matlab 2024b实现,结合粒子群优化(PSO)与XGBoost算法,用于时间序列预测。通过PSO优化XGBoost超参数,提升预测精度。程序包含完整注释与操作视频,运行后生成预测效果图及性能评估指标RMSE。
|
17天前
|
存储 算法 安全
【无人机】基于灰狼优化算法的无人机路径规划问题研究(Matlab代码实现)
【无人机】基于灰狼优化算法的无人机路径规划问题研究(Matlab代码实现)
109 0
|
17天前
|
机器学习/深度学习 数据采集 算法
【创新无忧】基于白鲨算法WSO优化广义神经网络GRNN电机故障诊断(Matlab代码实现)
【创新无忧】基于白鲨算法WSO优化广义神经网络GRNN电机故障诊断(Matlab代码实现)
|
18天前
|
算法 Java 调度
【车间调度】基于GA、PSO、SA、ACO、TS优化算法的车间调度比较研究(Matlab代码实现)
【车间调度】基于GA、PSO、SA、ACO、TS优化算法的车间调度比较研究(Matlab代码实现)

热门文章

最新文章