C++中的字符串匹配:一种高效的算法

2023-07-02 09:30:00 浏览数 (874)

字符串匹配是指在一个较长的字符串中查找一个较短的字符串的位置,这是一个常见的编程问题,也是许多应用程序的基础,比如文本编辑器、搜索引擎、数据压缩等。在本文中,我们将介绍一种在C++中进行字符串匹配的高效算法,即KMP算法。

KMP算法是由Donald Knuth、Vaughan Pratt和James H. Morris于1977年提出的,它的全称是Knuth-Morris-Pratt算法。KMP算法的核心思想是利用已经匹配过的部分字符串来避免重复比较,从而提高匹配效率。具体来说,KMP算法使用一个辅助数组(称为next数组)来记录每个位置之前的最长相同前缀后缀长度,这样当匹配失败时,可以根据next数组的值来移动模式串(较短的字符串),而不是从头开始比较。

KMP算法的实现步骤如下:

  1. 首先,根据模式串计算出next数组,这可以通过一个循环来完成,时间复杂度为O(m),其中m是模式串的长度。
  2. 然后,从左到右遍历主串(较长的字符串),用两个指针i和j分别指向主串和模式串的当前位置,初始时i和j都为0。
  3. 如果主串和模式串的当前字符相同,那么i和j都加一,继续比较下一个字符。
  4. 如果主串和模式串的当前字符不同,那么根据next数组的值来移动模式串,即令j=next[j],如果j变为-1,那么i加一,j变为0,重新开始比较。
  5. 重复步骤3和4,直到主串或模式串遍历完毕。
  6. 如果模式串遍历完毕,那么说明匹配成功,返回i-j作为匹配位置;如果主串遍历完毕,那么说明匹配失败,返回-1。

为了更好地理解KMP算法的过程,我们给出一个示例代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;


// 计算next数组
vector<int> getNext(string pattern) {
int m = pattern.size();
vector<int> next(m, -1); // 初始化为-1
int i = 0, j = -1; // i表示后缀末尾位置,j表示前缀末尾位置
while (i < m - 1) {
if (j == -1 || pattern[i] == pattern[j]) { // 如果相等或者j已经回退到-1
i++; // 后缀末尾位置后移一位
j++; // 前缀末尾位置后移一位
next[i] = j; // 记录当前位置之前的最长相同前缀后缀长度
} else {
j = next[j]; // 如果不相等,则回退到前一个最长相同前缀后缀长度
}
}
return next;
}


// KMP算法
int kmp(string text, string pattern) {
int n = text.size();
int m = pattern.size();
vector<int> next = getNext(pattern); // 计算next数组
int i = 0, j = 0; // i表示主串当前位置,j表示模式串当前位置
while (i < n && j < m) {
if (j == -1 || text[i] == pattern[j]) { // 如果相等或者j已经回退到-1
i++; // 主串当前位置后移一位
j++; // 模式串当前位置后移一位
} else {
j = next[j]; // 如果不相等,则回退到前一个最长相同前缀后缀长度
}
}
if (j == m) { // 如果模式串遍历完毕,说明匹配成功
return i - j; // 返回匹配位置
} else { // 如果主串遍历完毕,说明匹配失败
return -1; // 返回-1
}
}


int main() {
string text = "ababababca";
string pattern = "abababca";
int pos = kmp(text, pattern);
cout << "The position of pattern in text is: " << pos << endl;
return 0;
}

输出结果为:

The position of pattern in text is: 2

可以看到,KMP算法可以在O(n+m)的时间内找到模式串在主串中的位置,而不需要进行多余的比较。KMP算法是一种经典的字符串匹配算法,值得学习和掌握。