前言
数据结构中串模式匹配的C++语言描述实现模板,有详细的步骤解析及使用示例。
代码仓库
暴力/简单/朴素算法
代码模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <iostream>
using namespace std;
void useExample(); int index(const string &mainStr, const string &subStr);
void useExample() { const string mainStr = "abcde"; const string subStr = "cd";
cout << index(mainStr, subStr) << endl;
return; }
int index(const string &mainStr, const string &subStr) { int i = 0; int j = 0;
while (i < mainStr.size() && j < subStr.size()) { if (mainStr[i] == subStr[j]) { ++i; ++j; } else { i = i - j + 1; j = 0; } }
if (j == subStr.size()) { return i - subStr.size(); } else { return -1; } }
int main() { useExample();
return 0; }
|
复杂度
- 理论时间复杂度:O(n×m)。n为主串规模,m为子串规模
- 实际(一般情况下的)时间复杂度::O(n+m)。n为主串规模,m为子串规模
- 空间复杂度:O(1)。未使用辅助空间
KMP算法
核心思想
- 作用:字符串(主串和子串)匹配;字符串(主串中的子串间)重复
- 前缀:除最后一个字符外,字符串的首部连续子串
- 后缀:除第一个字符外,字符串的尾部连续子串
- 最长相等前后缀:待匹配字符前的子串中,最长且相等的前后缀子串
- 前缀表:记录最长相等前后缀的长度
- next数组:前缀表或值统一减一的前缀表
- next数组的值next [ j ]:子串的第 j 个字符与主串的当前字符匹配失败,跳转到子串的第next [ j ]个字符与主串的当前字符进行匹配(可能匹配失败或匹配成功)
- 时间复杂度:O(n+m)。n为主串规模,m为子串/next数组规模
- 空间复杂度:O(m)。m为子串/next数组规模
代码模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| #include <iostream> #include <vector>
using namespace std;
void useExample(); void getNext(const string &subStr, vector<int> &next); int indexKMP(const string &mainStr, const string &subStr, const vector<int> &next);
void useExample() { const string mainStr = "abcde"; const string subStr = "cd";
const int subStrSize = subStr.size();
vector<int> next(subStrSize, -1);
getNext(subStr, next);
cout << indexKMP(mainStr, subStr, next) << endl;
return; }
void getNext(const string &subStr, vector<int> &next) { int i = 0; int j = -1;
next[0] = -1;
while (i < subStr.size() - 1) {
if (j == -1 || subStr[i] == subStr[j]) { ++i; ++j;
next[i] = j; } else { j = next[j]; } }
return; }
int indexKMP(const string &mainStr, const string &subStr, const vector<int> &next) { int i = 0; int j = 0;
const int mainStrSize = mainStr.size(); const int subStrSize = subStr.size();
while (i < mainStrSize && j < subStrSize) { if (j == -1 || mainStr[i] == subStr[j]) { ++i; ++j; } else { j = next[j]; } }
if (j == subStr.size()) { return i - subStr.size(); } else { return -1; } }
int main() { useExample();
return 0; }
|
KMP改进算法
核心思想
- 解决子串中匹配失败的字符和回溯位置的字符相同导致的冗余匹配问题
- 时间复杂度:O(n+m)。n为主串规模,m为子串/nextval数组规模
- 空间复杂度:O(m)。m为子串/nextval数组规模
代码模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| #include <iostream> #include <vector>
using namespace std;
void useExample(); void getNextval(const string &subStr, vector<int> &nextval); int indexKMP(const string &mainStr, const string &subStr, const vector<int> &nextval);
void useExample() { const string mainStr = "abcde"; const string subStr = "cd";
const int subStrSize = subStr.size();
vector<int> nextval(subStrSize, -1);
getNextval(subStr, nextval);
cout << indexKMP(mainStr, subStr, nextval) << endl;
return; }
void getNextval(const string &subStr, vector<int> &nextval) { int i = 0; int j = -1;
nextval[0] = -1;
while (i < subStr.size() - 1) {
if (j == -1 || subStr[i] == subStr[j]) { ++i; ++j;
if (subStr[i] != subStr[j]) { nextval[i] = j; } else { nextval[i] = nextval[j]; } } else { j = nextval[j]; } }
return; }
int indexKMP(const string &mainStr, const string &subStr, const vector<int> &nextval) { int i = 0; int j = 0;
const int mainStrSize = mainStr.size(); const int subStrSize = subStr.size();
while (i < mainStrSize && j < subStrSize) { if (j == -1 || mainStr[i] == subStr[j]) { ++i; ++j; } else { j = nextval[j]; } }
if (j == subStr.size()) { return i - subStr.size(); } else { return -1; } }
int main() { useExample();
return 0; }
|
总结
不同参考资料中,串模式匹配的描述实现各不相同,但基本思想是一致的。作者使用规范的变量命名、提供详细的步骤解析及使用示例,应用C++语言将其整合成模板,以帮助理解记忆。
参考资料
- 《2023版数据结构高分笔记》主编:率辉
- 《2023年计算机组成原理考研复习指导》组编:王道论坛
- 《大话数据结构》作者:程杰
作者的话
- 感谢参考资料的作者/博主
- 作者:夜悊
- 版权所有,转载请注明出处,谢谢~
- 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
- 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
- 文章在认识上有错误的地方, 敬请批评指正
- 望读者们都能有所收获