Sunday 算法快速入门
Sunday 算法是 Daniel M.Sunday 于 1990 年提出的字符串模式匹配算法,该算法的核心思想与 BM 算法类似。
通过本文章我可以学到什么
Sunday算法思想
实现
优点
局限性
举个栗子
Sunday 算法匹配方式如下:
例 1
- 令字符串
txt为ABCDABA - 令模式串
pat为ABA
则刚开始匹配时txt与pat左端对齐

此时我们从pat的左端开始向右匹配,当匹配到第三位时txt与pat的内容不同:

接着检查txt中pat的长度下一位字母在pat中有没有出现过。
发现 并没有在pat中出现,便将整个pat向后移动 位:

此时便匹配成功。
例 2
txt = ABBBABBABApat = ABA
初始状态:

匹配时发现在第 位时txt与pat不一样,于是便比较txt的 位,发现 在pat中出现过,于是便将该处 与pat中最接近末尾的 对齐:

匹配时发现匹配失败了,于是检查下一位 在pat中出现过,将它与pat中最靠近末尾的 对齐:

检查下一位,对齐:

匹配成功。
算法思想
- 从头开始匹配
- 匹配到不一样时对比
txt中整个pat长度的下一位是否在pat中出现过- 出现则跳到离末端最近
- 未出现则整个
pat跳过
时间复杂度
代码实现
令nowTxt代表目前txt中匹配对应pat的首字母所在的位置,nowPat为匹配到的pat的第几位。
pat匹配
while (txt[nowTxt + nowPat] == pat[nowPat]) {
++nowPat; // 对应位置匹配后将 检测的位置 向后移动
if (nowPat == pat.length()) { // 完全匹配整个pat字符串则返回对应的pat所在位置
return nowTxt;
break;
}
}移动位数
array<int, 26> sundayArray; // 一共26个字母,则开数组为26即可(可以根据自己需要进行修改)
inline void getSundayArray(const string &pat) {
int len = pat.length(); // 获取pat的长度
fill(sundayArray.begin(), sundayArray.end(), -1); // 初始化数组为 -1
for (int i = len - 1, cnt = 0; i >= 0 && cnt < 26; ++i) { // 倒叙枚举,保证正确性(使对应字母离尾端的距离位最小值)
int j = pat[i] - 'A'; // 获取pat对应位上的字母编号
if (sundayArray[j] == -1) { // 如果之前并未修改,说明之前并没有出现对应字母
sundayArray[j] = len - i; // 修改
++cnt; // 计数,如果已经拥有26个字母则跳出
}
}
}进行匹配
inline int sunday(const string &txt, const string &pat) {
int n = txt.length(); // 获取txt长度
int m = pat.length(); // 获取pat长度
int nowTxt = 0, nowPat; // 定义变量(指针)
while (nowTxt + m <= n) { // 直到不能匹配为止
nowPat = 0; // 初始化pat对应位置位开头(即0)
while (txt[nowTxt + nowPat] == pat[nowPat]) {
++nowPat;
if (nowPat == m) {
return nowTxt;
}
}
if (nowTxt + m >= n) { // 超限则输出无解(-1)
return -1;
}
if (sundayArray[txt[nowTxt + m] - 'A'] != -1) {
nowTxt += sundayArray[txt[nowTxt + m] - 'A'];
} else {
nowTxt += m + 1;
}
}
}局限性
当字符串txt与模式串pat具有特定的关系时,Sunday算法的时间复杂度可能会退化为