本文共 7405 字,大约阅读时间需要 24 分钟。
串,String,是由零个或多个字符组成的有限序列
,一般记为:S=‘abcd……’,其中S称为串名
,单引号括起来的字符序列就是串的值,序列中的元素可以是字母、数字或其他可打印字符,元素个数 n 称为串的长度,当且仅当 n=0 时字符串才是空串。若字符序列是由若干空格构成,称为空格串,其长度 n 为空格个数。\0
。顺序存储、堆存储和块链存储
。块
,整个链表称为块链结构
,如下图: 串结构的基本操作如下:
#pragma once#include#include using namespace std;class HeapStr{ private: char* data; int length;public: HeapStr(); bool StrAssign(string s); void Show(); void StrCopy(HeapStr s); bool StrEmpty(); int StrCompare(HeapStr s); int StrLength(); void SubStr(HeapStr& sub, int pos, int len); void Concat(HeapStr s1, HeapStr s2); int Index(HeapStr sub); bool ClearStr();};// 构造串HeapStr::HeapStr(){ data = NULL; length = 0;}bool HeapStr::StrAssign(string s){ int len = s.length(); try { data = (char*)malloc((len + 1) * sizeof(char)); for (int i = 0; i < len; i++) data[i] = s[i]; data[len] = '\0'; length = len; return true; } catch (exception e) { cout << e.what() << endl; return false; }}// 判空bool HeapStr::StrEmpty(){ if (data == NULL) { cout << "未初始化赋值" << endl; return true; } if (length == 0) return true; return false;}int HeapStr::StrLength(){ if (data == NULL) { cout << "未初始化赋值" << endl; return -1; } else if (this->StrEmpty()) { cout << "串空!" << endl; return 0; } return length;}void HeapStr::Show(){ if (data == NULL) { cout << "未初始化赋值" << endl; return; } else if (this->StrEmpty() || data[0] == '\0') { cout << "串空!" << endl; exit(0); } cout << data << endl;}void HeapStr::StrCopy(HeapStr s){ if (s.length == 0) { cout << "参数为空串!" << endl; return; } if (s.data == NULL) { cout << "参数错误,未初始化赋值" << endl; exit(0); } int len = s.length; if (this->length < len) { cout << "参数串太长,将发生截断" << endl; for (int i = 0; i < length; i++) data[i] = s.data[i]; data[length] = '\0'; } for (int i = 0; i < len; i++) data[i] = s.data[i]; data[len] = '\0';}int HeapStr::StrCompare(HeapStr s){ if (data == NULL && s.data == NULL) { cout << "未初始赋值!" << endl; exit(0); } if (this->StrEmpty() == s.StrEmpty()) return 0; int i; if (length < s.length) { for (i = 0; i < length; i++) { if (data[i] > s.data[i]) return 1; else if (data[i] < s.data[i]) return -1; } if (i == length) return 0; } for (i = 0; i < s.length; i++) { if (data[i] > s.data[i]) return 1; else if (data[i] < s.data[i]) return -1; } if (i == s.length) return 0;}void HeapStr::SubStr(HeapStr& s, int pos, int len){ if (data == NULL) { cout << "未初始化赋值" << endl; return; } else if (this->StrEmpty()) { cout << "串空!" << endl; return; } for (int i = 0; i < len; i++) { s.data[i] = data[i + pos - 1]; } s.data[len] = '\0';}void HeapStr::Concat(HeapStr s1, HeapStr s2){ if (data == NULL) { cout << "未初始化赋值" << endl; return; } for (int i = 0; i < s1.length; i++) data[i] = s1.data[i]; for (int j = 0; j < s2.length; j++) data[j + s1.length] = s2.data[j]; data[s1.length + s2.length - 1] = '\0';}int HeapStr::Index(HeapStr sub){ if (sub.length > length) return -1; int i = 0, j = 0; while (i < length && j < sub.length) { if (data[i] == sub.data[j]) { ++i; ++j; } else { i = i - j + 2; j = 0; } } if (j > sub.length) return i - sub.length; else return 0;}bool HeapStr::ClearStr(){ if (data == NULL) { cout << "串未初始化赋值" << endl; exit(0); } if (length == 0) { return true; } data[0] = '\0'; length = 0; return true;}
查找较短的一个串在较长的一个串中第一次出现的位置
,简单来说就是确定子串在母串中的位置,子串通常称为模式串
,母串则称主串
。暴力算法、KMP 算法和改进的 KMP 算法
。O(mn)
;一旦遇到母串和子串都比较长,那么时间就需要很久了。int HeapStr::Index(HeapStr sub){ if (sub.length > length) return -1; int i = 0, j = 0; while (i < length && j < sub.length) { if (data[i] == sub.data[j]) { ++i; ++j; } else { i = i - j + 2; j = 0; } } if (j > sub.length) return i - sub.length; else return 0;}
如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式向后滑动到与这些相等字符对齐的位置,主串立指针无须回溯,并继续从该位置开始进行比较。
而模式向后滑动位数的计算仅与模式本身的结构有关,与主串无关。除最后一个字符外,字符串的所有头部子串
。除第一个字符外,字符串的所有尾部子串
。字符串的前缀和后缀的最长相等前后缀长度
,通俗理解就是前缀集与后缀集的交集中的长度最长的元素的字符个数。'ab' 的前缀为{a},后缀为{b},前缀集与后缀集交集为空集,最长相等前后缀就是 0.'aba' 的前缀为{a,ab},后缀为{a,ba},两者交集为{a},最长相等前后缀就是 1.'ababa'的前缀为{a,ab,aba,abab},后缀{a,ba,aba,baba},两者交集为{a,aba},最长相等前后缀为 3
移动位数=已匹配的字符数 - 对应的部分匹配值
。ababcabcacbab
,和模式串:abcac
为例。 next[j] 表明当模式中第 j 个字符与主串中相应字符不相等时,在模式中需要重新和主串中该字符进行比较的字符的位置
。j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
模式 | a | b | a | a | b | c | a | b | a |
next[j] | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 | 3 |
// 模式匹配 KMP 算法int HeapStr::IndexKMP(HeapStr sub){ int len = sub.length; int* next = new int[len + 1]; for (int k = 0; k <= len; k++) { next[k] = 0; } int i = 1, j = 0; while (i < len) { if (j == 0 || sub.data[i] == sub.data[j]) { ++i; ++j; next[i] = j; } else j = next[j]; } i = 1, j = 1; while (i <= this->length && j <= sub.length) { if (j == 0) { ++i; ++j; } else if (j != 0 && this->data[i - 1] == sub.data[j - 1]) { ++i; ++j; } else j = next[j]; } if (j > sub.length) return i - sub.length; else return -1;}
KMP 算法仅在主串与子串有很多部分匹配时才显得比普通算法快很多
,其主要优点是主串不回溯。next
得算法尚有缺陷,尤其是处理 pj!=pk 时,第 k 个字符得 next[j] 所在的字符与pk 一样因而也不等于 pj,此时显然需要进行递归,即将 next[j] 改为 next[next[j]] ,直到两个不相等为止,为了与原来的算法区别,此时用于确定模式串滑动位置的数组命名为 nextval
。while (i < len) { if (j == 0 || sub.data[i] == sub.data[j]) { ++i; ++j; if (sub.data[i] != sub.data[j]) nextval[i] = j; else nextval[i] = nextval[j]; } else j = nextval[j]; }
转载地址:http://mqqgn.baihongyu.com/