定义

字符串简称串,计算机上非数值处理的对象基本都是字符串数据。

子串查找算法(模式匹配算法)

从一个串中查找另一个子串(模式串)的过程

BF算法

比较"暴力"的字符串匹配算法,效率低。

算法复杂度分析

设主串的长度为n,子串的长度为m

时间复杂度 空间复杂度
O(n*m) O(1)

代码实现

cpp 复制代码
#include <iostream>

using namespace std;

int BF(string s, string t) {
  int i = 0;  // 主串下标
  int j = 0;   // 子串下标

  while (i < s.size() && j < t.size()) {

    if (s[i] == t[j]) {  // 对应字符相等
      i++;
      j++;
    } else {  // 不相等
      i = i - j + 1; // 复位i, 指向之前开始匹配字符的下一个字符
      j = 0;
    }

  }

  // 子串在主串中找到了
  if (j == t.size()) {
    return i - j;
  }

  return -1;

}

int main() {

  string s = "ABCDCABDEFG";
  string t = "ABD";

  // 找到子串,返回子串在主串中的起始下标,如果找不到,返回-1
  int pos = BF(s, t);

  cout << "pos = " << pos << endl;
  
  return 0;
}

测试

sh 复制代码
➜  build git:(main) ✗ ./BFString 
pos = 5

KMP算法

  • BF算法中对于子串的形状没有做任何的分析,导致匹配过程中做了很多无效的匹配操作,导致算法效率的降低。KMP算法进行了改进,在匹配过程中主串不用回退,提高了算法效率。

  • KMP算法的核心思想就是zifu失配后,主串的i不做回退操作,只回退子串的j。由于在任意一个字符匹配时都有可能失配,所以KMP算法的关键就是给子串计算出一个next数组,里面存储的是当前字符失配时j要回退到的位置,也就是存储的是当前字符前面的子串的公共前后缀的长度。

算法复杂度分析

设主串的长度为n,子串的长度为m

时间复杂度 空间复杂度
O(n + m) O(m)

代码

cpp 复制代码
#include <iostream>
#include <memory>
using namespace std;

// KMP算法求解子串的next数组
int* getNext(string str) {
  int* next = new int[str.size()];

  int j = 0;  // j用来遍历子串
  int k = -1; // k 表示公共前后缀的长度

  next[j] = k;  // 第0个位置设置为-1

  while (j < str.size() - 1) {
    if (-1 == k ||  str[k] == str[j]) {
      j++;
      k++;
      next[j] = k;
    } else {
      // 不相等,做k值的回退
      k = next[k];
    }
  }

  return next;

}

int KMP(string s, string t) {
  int i = 0;  // 主串下标
  int j = 0;   // 子串下标

  // 计算一个子串对应的next数组
  int* next = getNext(t);

  unique_ptr<int> ptr(next);  // delete掉next数组,释放内存

  int size1 = s.size();
  int size2 = t.size();

  while (i < size1 && j < size2) {

    if (-1 == j || s[i] == t[j]) {  // 对应字符相等
      i++;
      j++;
    } else {  // 不相等
      // KMP的核心是不回退i值,只回退j值
      j = next[j];   // 如果首字母匹配失败,这里j = -1
    }

  }

  // 子串在主串中找到了
  if (j == t.size()) {
    return i - j;
  }

  return -1;

}

int main() {

  string s = "ABCDCABDEFG";
  string t = "ABD";

  // 找到子串,返回子串在主串中的起始下标,如果找不到,返回-1
  int pos = KMP(s, t);

  cout << "pos = " << pos << endl;
  
  return 0;
}

测试

sh 复制代码
➜  build git:(main) ✗ ./KMPString    
pos = 5

KMP算法优化

优化关键是优化next数组

cpp 复制代码
// KMP算法求解子串的next数组
int* getNext(string str) {
  int* next = new int[str.size()];

  int j = 0;  // j用来遍历子串
  int k = -1; // k 表示公共前后缀的长度

  next[j] = k;  // 第0个位置设置为-1

  while (j < str.size() - 1) {
    if (-1 == k ||  str[k] == str[j]) { 
      j++;
      k++;

      // KMP算法优化
      if (str[k] == str[j]) {
        next[j] = next[k];  // 关键,回退的优化
      } else {
        next[j] = k;
      }

    } else {
      // 不相等,做k值的回退
      k = next[k];
    }
  }

  return next;

}
相关推荐
luckys.one1 天前
第9篇:Freqtrade量化交易之config.json 基础入门与初始化
javascript·数据库·python·mysql·算法·json·区块链
~|Bernard|1 天前
在 PyCharm 里怎么“点鼠标”完成指令同样的运行操作
算法·conda
战术摸鱼大师1 天前
电机控制(四)-级联PID控制器与参数整定(MATLAB&Simulink)
算法·matlab·运动控制·电机控制
Christo31 天前
TFS-2018《On the convergence of the sparse possibilistic c-means algorithm》
人工智能·算法·机器学习·数据挖掘
好家伙VCC1 天前
数学建模模型 全网最全 数学建模常见算法汇总 含代码分析讲解
大数据·嵌入式硬件·算法·数学建模
liulilittle1 天前
IP校验和算法:从网络协议到SIMD深度优化
网络·c++·网络协议·tcp/ip·算法·ip·通信
bkspiderx1 天前
C++经典的数据结构与算法之经典算法思想:贪心算法(Greedy)
数据结构·c++·算法·贪心算法
中华小当家呐1 天前
算法之常见八大排序
数据结构·算法·排序算法
沐怡旸1 天前
【算法--链表】114.二叉树展开为链表--通俗讲解
算法·面试
一只懒洋洋1 天前
K-meas 聚类、KNN算法、决策树、随机森林
算法·决策树·聚类