字符串匹配算法:Sunday算法
背景
我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是\(Ω(m*n)\),也就是达到了字符串匹配效率的下限。于是后来人经过研究,构造出了著名的KMP算法(Knuth-Morris-Pratt算法),让我们的时间复杂度降低到了\(O(m+n)\),但现代文字处理器中,却很少使用KMP算法来做字符串匹配,因为还是太慢了。现在主流的算法是BM算法(Boyer-Moore算法),成功让平均时间复杂度降低到了\(O(m/n)\),而Sunday算法,则是对BM算法的进一步小幅优化。
KMP算法很多人看了一遍遍以后,对next[n]数组的理解还是有点困难(包括笔者),写代码的时候总是容易变成这种情况(/捂脸.jpg):
(切到网页):马冬梅
(切到编译器):马什么梅
(切到网页):马冬梅
(切到编译器):马冬什么
(切到网页):马冬梅
(切到编译器):什么冬梅
而Sunday算法,理解起来则是非常容易,同时极低的时间复杂度,让Sunday算法成为了我目前最常使用的字符串匹配算法
Sunday 算法是 Daniel M.Sunday 于 1990 年提出的字符串模式匹配。其效率在匹配随机的字符串时比其他匹配算法还要更快。Sunday 算法的实现可比 KMP,BM 的实现容易太多。
平均性能的时间复杂度为\(O(n)\);
最差情况的时间复杂度为\(O(n * m)\)。
算法过程
Sunday算法和BM算法稍有不同的是,Sunday算法是从前往后匹配,在匹配失败时关注的是主串中参加匹配的最末位字符的下一位字符。
- 如果该字符没有在模式串中出现则直接跳过,即移动位数 = 模式串长度 + 1;
- 否则,其移动位数 = 模式串长度 - 该字符最右出现的位置(以0开始) = 模式串中该字符最右出现的位置到尾部的距离 + 1。
现在举个例子讲解Sunday算法
假定主串为 "HERE IS A SIMPLE EXAMPLE",模式串为 "EXAMPLE"。
(1)

从头部开始比较,发现不匹配。则 Sunday 算法要求如下:找到主串中位于模式串后面的第一个字符,即红色箭头所指的 "空格",再在模式串中从后往前找 "空格",没有找到,则直接把模式串移到 "空格" 的后面。
(2)

依旧从头部开始比较,发现不匹配。找到主串中位于模式串后面的第一个字符 L,模式串中存在 L,则移动模式串使两个 L 对齐。
(3)

找到匹配。
完整代码
#include <iostream>
#include <string>
#define MAX_CHAR 256
#define MAX_LENGTH 1000
using namespace std;
void GetNext(string & p, int & m, int next[])
{
for (int i = 0; i < MAX_CHAR; i++)
next[i] = -1;
for (int i = 0; i < m; i++)
next[p[i]] = i;
}
void Sunday(string & s, int & n, string & p, int & m)
{
int next[MAX_CHAR];
GetNext(p, m, next);
int j; // s 的下标
int k; // p 的下标
int i = 0;
bool is_find = false;
while (i <= n - m)
{
j = i;
k = 0;
while (j < n && k < m && s[j] == p[k])
j++, k++;
if (k == m)
{
cout << "在主串下标 " << i << " 处找到匹配\n";
is_find = true;
}
if (i + m < n)
i += (m - next[s[i + m]]);
else
break;
}
if (!is_find)
cout << "未找到匹配\n";
}
int main()
{
string s, p;
int n, m;
while (cin >> s >> p)
{
n = s.size();
m = p.size();
Sunday(s, n, p, m);
cout << endl;
}
return 0;
}
数据测试如下:
here#is#a#example
example
在主串下标 10 处找到匹配
aaa
a
在主串下标 0 处找到匹配
在主串下标 1 处找到匹配
在主串下标 2 处找到匹配
aaa
b
未找到匹配
Sunday算法的缺点
看上去简单高效非常美好的Sunday算法,也有一些缺点。因为Sunday算法的核心依赖于move数组,而move数组的值则取决于模式串,那么就可能存在模式串构造出很差的move数组。例如下面一个例子
主串:baaaabaaaabaaaabaaaa
模式串:aaaaa
这个模式串使得move[a]的值为1,即每次匹配失败时,只让模式串向后移动一位再进行匹配。这样就让Sunday算法的时间复杂度飙升到了O(m*n),也就是字符串匹配的最坏情况,在这种情况下效率就明显低于KMP等算法了 例如:HDU1686
总结
当然,也不能因为存在最坏的情况就直接否定Sunday算法,大多数情况下,Sunday依然是一个简单高效的算法,值得我们熟练学习掌握。
字符串匹配算法:Sunday算法的更多相关文章
- 字符串匹配算法 -- Rabin-Karp 算法
字符串匹配算法 -- Rabin-Karp 算法 参考资料 1 算法导论 2 lalor 3 记忆碎片 Rabin-karp 算法简介 在实际应用中,Rabin-Karp 算法对字符串匹配问题能较好的 ...
- 字符串匹配算法——KMP算法
处理字符串的过程中,难免会遇到字符匹配的问题.常用的字符匹配方法 1. 朴素模式匹配算法(Brute-Force算法) 求子串位置的定位函数Index( S, T, pos). 模式匹配:子串的定位操 ...
- 字符串模式匹配sunday算法
文字部分转自:http://www.cnblogs.com/mr-ghostaqi/p/4285868.html 代码是我自己写的 今天在做LeetCode的时候,碰到一个写字符串匹配的题目: htt ...
- 字符串匹配算法——KMP算法学习
KMP算法是用来解决字符串的匹配问题的,即在字符串S中寻找字符串P.形式定义:假设存在长度为n的字符数组S[0...n-1],长度为m的字符数组P[0...m-1],是否存在i,使得SiSi+1... ...
- 字符串匹配算法KMP算法
数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法. 朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多 ...
- [Algorithm] 字符串匹配算法——KMP算法
1 字符串匹配 字符串匹配是计算机的基本任务之一. 字符串匹配是什么?举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串& ...
- 独树一帜的字符串匹配算法——RK算法
参加了雅虎2015校招,笔试成绩还不错,谁知初面第一题就被问了个字符串匹配,要求不能使用KMP,但要和KMP一样优,当时瞬间就呵呵了.后经过面试官的一再提示,也还是没有成功在面试现场写得.现将该算法记 ...
- 字符串匹配算法-kmp算法
一原理: 部分转自:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 字 ...
- 字符串匹配算法--Brute-Force算法
Brute-Force(暴力)算法是字符串匹配中最简单也是最容易理解的算法. 主要思想是 按顺序遍历母串,将每个字符作为匹配的起始字符,判断是否匹配字串.若第一个字符与字串匹配,则比较下一个字符,否则 ...
- Sunday算法:字符串匹配算法进阶
背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是\(Ω(m*n)\),也就是达到了字符串匹配效率的下限.于是后来人经过研究,构造出了著名的KMP算法 ...
随机推荐
- MVC-基础01
MVC体系结构将应用程序分成三个主要组件:模型(Model).视图(View).和控制器(Controller).在ASP.NET MVC应用程序中,数据操控的逻辑包含在Models文件夹下,数据的展 ...
- PHP 新特性:如何善用接口与Trait
首先! 接口也可以继承,通过使用 extends 操作符. 案例: <?php interface a { public function foo(); } interface b extend ...
- Linux 获取网卡名字列表
lspci | egrep -i --color 'network|ethernet'
- http 之 CORS简介
什么是CORS? CORS:跨域资源共享.是一种机制. 用处? 它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定 ...
- Linux硬盘分区知识
前言 硬盘使用前,一般要分区,格式化(创建文件系统)<== 存放数据 类比,房子使用前,一般要隔断,装修,买家具,再住人. 分区 一块硬盘: 主分区.扩展分区.逻辑分区 主分区+扩展分区的数量& ...
- Java反射详细介绍
反射 目录介绍 1.反射概述 1.1 反射概述 1.2 获取class文件对象的三种方式 1.3 反射常用的方法介绍 1.4 反射的定义 1.5 反射的组成 1.6 反射的作用有哪些 2.反射的相关使 ...
- 【万字长文】别再报班了,一篇文章带你入门Python
本文始发于个人公众号:TechFlow,原创不易,求个关注 最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不 ...
- Knapsack Problem
0-1背包 描述:N件物品,第i件的重量是w[i],价值v[i].有一个容量为W的背包,求将哪些物品放入背包可使总价值最大.每件物品可以用0或1次. 分析:根据题意,可以写出表达式: \[max(\S ...
- THU OS
前言 最近在学清华的OS课程,实验课做的真厉害. 我们的操作系统课就是写一个系统调用,改下进程软中断通信的代码,代码量不足500...清华是借鉴MIT的课,实验课都是做一个完整的小型操作系统. 一共8 ...
- Pattern Matching
字符串模式匹配,即子串的定位操作.就是判断主串S中是否存在给定的子串,如果存在,那么返回子串在S中的位置,否则返回0. 实现这种操作有两种算法: 朴素的模式匹配算法 设主串S长度为n,子串T长度为m. ...