转自:http://blog.csdn.net/zdl1016/archive/2009/10/11/4654061.aspx
我想说一句“我日,我讨厌KMP!”。
KMP虽然经典,但是理解起来极其复杂,好不容易理解好了,便起码来巨麻烦!
老子就是今天图书馆在写了几个小时才勉强写了一个有bug的、效率不高的KMP,特别是计算next数组的部分。
其实,比KMP算法速度快的算法大把大把,而且理解起来更简单,为何非要抓住KMP呢?笔试出现字符串模式匹配时直接上sunday算法,既简单又高效,何乐而不为?
说实话,想到sunday算法的那个人,绝对是发散思维,绝对牛。当我在被KMP折磨的够呛的时候,我就琢磨,有没有别的好算法呢??琢磨了半天也没想出个所以然来。笨啊,脑子不够发散。
下面贴上一位兄弟写的算法总结,很简单(建议KMP部分就不用看了,看了费脑子)。
参见:http://hi.baidu.com/willamette/blog/item/02bd0b5599c8b4c0b645ae06.html
趁着做Presentation的功夫,顺便做一个总结
字符串匹配:
---willamette
在匹配串中寻找模式串是否出现,注意和最长公共子序列相区别(LCS: Longest Common Substring)
-:Brute Force(BF或蛮力搜索)算法:
这是世界上最简单的算法了。
首先将匹配串和模式串左对齐,然后从左向右一个一个进行比较,如果不成功则模式串向右移动一个单位。
速度最慢。
那么,怎么改进呢?
我们注意到Brute Force算法是每次移动一个单位,一个一个单位移动显然太慢,是不是可以找到一些办法,让每次能够让模式串多移动一些位置呢?
当然是可以的。
我们也注意到,Brute Force是很不intelligent的,每次匹配不成功的时候,前面匹配成功的信息都被当作废物丢弃了,当然,就如现在的变废为宝一样,我们也同样可以将前面匹配成功的信息利用起来,极大地减少计算机的处理时间,节省成本。^_^
注意,蛮力搜索算法虽然速度慢,但其很通用,文章最后会有一些更多的关于蛮力搜索的信息。
-: KMP算法
首先介绍的就是KMP算法。
原始论文:Knuth D.E., Morris J.H., and Pratt V.R., Fast pattern matching in strings, SIAM Journal on Computing, 6(2), 323-350, 1977.
这个算法实在是太有名了,大学上的算法课程除了最笨的Brute Force算法,然后就介绍了KMP算法。也难怪,呵呵。谁让Knuth D.E.这么world famous呢,不仅拿了图灵奖,而且还写出了计算机界的Bible <The Art of Computer Programming>(业内人士一般简称TAOCP).稍稍提一下,有个叫H.A.Simon的家伙,不仅拿了Turing Award,顺手拿了个Nobel Economics Award,做了AI的爸爸,还是Chicago Univ的Politics
PhD,可谓全才。
KMP的思想是这样的:
利用不匹配字符的前面那一段字符的最长前后缀来尽可能地跳过最大的距离
比如
模式串ababac 这个时候我们发现在c处不匹配,然后我们看c前面那串字符串的最大相等前后缀,然后再来移动
下面的两个都是模式串,没有写出来匹配串
原始位置 ababac
移动之后 ababac
因为后缀是已经匹配了的,而前缀和后缀是相等的,所以直接把前缀移动到原来后缀处,再从原来的c处,也就是现在的第二个b处进行比较。 这就是KMP。
-:Horspool算法
Horspool算法。
当然,有市场就有竞争,字符串匹配这么大一个市场,不可能让BF和KMP全部占了,于是又出现了几个强劲的对手。
第一个登场的是
论文:Horspool R.N., 1980, Practical fast searching in strings, Software - Practice & Experience, 10(6):501-506
Horspool算法的思想很简单的。不过有个创新之处就是模式串是从右向左进行比较的。很好很强大,为后来的算法影响很大。
匹配串:abcbcsdxzcxx
模式串:cbcac
这个时候我们从右向左进行对暗号,c-c,恩对上了,第二个b-a,不对啊,我们应该怎么办?难道就这么放弃么。于是,模式串从不匹配的那个字符开始从右向左寻找匹配串中不匹配的字符b的位置,结果发现居然有,赶快对上赶快对上,别耽误了。
匹配串:abcbcsdxzcxx
模式串: cbcac
然后继续从最右边的字符从右向左进行比较。这时候,我们发现了,d-c不匹配啊,而且模式穿里面没有噢,没办法,只好移动一个模式串长度的单位了。
匹配串:abcbcsdxzcxx
模式串: cbcac
-:Boyer-Moore算法
第二个上来的是Boyer-Moore算法。
是一个很复杂的算法,当然,虽然理论上时间复杂度和KMP差不多,但是实际上却比KMP快数倍,可见实践是检验真理的唯一标准。
原始论文:R.S.Boyer, J.S.Moore, A fast string searching algorithm , Communications of the ACM,20(10):762-772 ,1977
分为两步预处理,第一个是bad-character heuristics,也就是当出现错误匹配的时候,移位,基本上就是做的Horspool那一套。
第二个就是good-suffix heuristics,当出现错误匹配的时候,我还要从不匹配点向左看啊,以前匹配的那段子字符串是不是在模式串本身中还有重复的啊,有重复的话,那么我就直接把重复的那段和匹配串中已经匹配的那一段对齐就是了。再比较
匹配串:abaccbabbazz
模式串:cbadcba
我们看到已经匹配好了cba,但是c-d不匹配,这个时候我们发现既可以采用bad-character heuristics,也可以使用good-suffix heuristics(模式串:cbadcba),在这种情况下,邪不压正。毅然投奔good。移动得到
匹配串:abaccbabbazz
模式串: cbadcba
可是,我们有时候也发现,已经匹配好的那一部分其实并没有再有重复了的啊。这个时候,我们发现已经匹配好的那串字符串有一部分在开头重新出现了,那么,赶快,对齐吧。
匹配串:abacccbbbazz
模式串:cbadccb
然后得到
匹配串:abacccbbbazz
模式串: cbadccb
当两种Good-Suffix出现的时候,取移动距离最大的那个。
(
对于BM算法,好规则和坏规则,这里讲的不够明确,下面推荐一个讲解非常优秀的文章,可谓图文并茂啊,而且还是个MM写的。
Boyer-Moore 经典单模式匹配算法
http://blog.csdn.net/iJuliet/archive/2009/05/19/4200771.aspx
)
-:Sunday算法
最后一个是Sunday算法,实际上比Boyer-Moore还快,呵呵。长江后浪推前浪。
原始论文:Daniel M. Sunday, A very fast substring search algorithm, Communications of the ACM, v.33 n.8, p.132-142, Aug. 1990
看原始论文的题目,D.M. Sunday貌似是故意想气气Boyer-Moore两位大牛似的。呵呵。不过实际上的确Sunday算法的确比BM算法要快,而且更简单。
Sunday的算法思想和Horspool有些相似,但是。当出现不匹配的时候,却不是去找匹配串中不匹配的字符在模式串的位置,而是直接找最右边对齐的右一位的那个字符在模式串的位置。
比如:
匹配串:abcbczdxzc
模式串:zbcac
恩,这里我们看到b-a没有对上,我们就看匹配串中的z在模式串的位置,然后,嘿嘿。
匹配串:abcbczdxzc
模式串: zbcac
如果模式串中的没有那个字符怎么办呢?很简单,跳过去呗。
匹配串:abcbcedxzcs
模式串:zbcac
e不在模式串中出现
那么我们就
匹配串:abcbcedxzcs
模式串: zbcac
(2009/10/20补充)
RK算法
某一天在图书馆的一本算法分析设计书上翻到的。思路很新颖!和大家分享下。
在串匹配的简单算法中,把文本每m个字符构成的字符段作为一个字段,和模式进行匹配检查。如果能对一个长度为m的字符
串赋以一个Hash函数。那么显然只有那些与模式具有相同hash函数值的文本中的字符串才有可能与模式匹配,这是必要条件
,而没有必要去考虑文本中所有长度为m的字段,因而大大提高了串匹配的速度。因此RK算法的思想和KMP,BM,Sunday等思
路迥然不同!
(事实上,之前的串匹配方法,是将模式串的一个一个字符作为小的特征去分别进行匹配,而RK算法则是将串整体作为一个
特征!难就难在单个字符的特征很容易想得到,整体作为一个特征就没那么容易想得到了)
如果把整体作为一个特征,那么如何快速的求出这个整体特征的特征值??
模式串的特征值仅需求一次即可。对于文本中的任意m个字符构成的字串如何快速的求特征就是个难点了。
抛砖引玉,这里给出一个简单的特征计算。 将字符串的每一个字符看做一个数,那么这个字符串的就是一个数字数组,通
过积分向量可以快速任意一个长度子字符串的向量和。可以把字符串的对应的字符数组的元素和看做这个字符串整体特征。
这个特征是可以再O(1)的时间内求出的。其实原始的RK算法里面是把字符串看做一个26进制数在计算特征的。这里就不啰
嗦了,有兴趣的可以深入查找
aabseesds 模式串 ees
ees
发现 see向量和 == ees的向量和
然后就对see和ees做逐个字符的比较。发现不匹配继续往下走
aabseesds 模式串 ees
ees
发现 ees向量和 == ees的向量和
然后就对ees和ees做逐个字符的比较。发现匹配OK。
另外还有 字符串匹配自动机 后缀树算法(分在线和非在线两种)等 见如下文章。不能说那个比那个更好,各个算法都有自己的优势及最佳应用场合。参考:
http://blog.csdn.net/yifan403/archive/2009/06/16/4272793.aspx
另外,关于多模式字符串匹配 有AC算法(字符串匹配自动机思想) WM算法(BM在多模式的推广应用)
参考:
http://blog.csdn.net/ijuliet/category/498465.aspx该女子的blog有很多好文章。
/**********************华丽分割线******************************/
附上sunday代码:
http://hi.baidu.com/kmj0217/blog/item/6f837f2f3da097311e3089cb.html
一种比KMP和BM更高效的匹配算法(如果想看原英文介绍,看下面分割线后的网址)
适用于:模式串较短的情况,最坏时间复杂性为O(N*M),不过一般没这么坏
Sunday算法其实思想跟BM算法很相似,只不过Sunday算法是从前往后匹配,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。如果该字符没有在匹配串中出现则直接跳过,即移动步长= 匹配串长度+ 1;否则,同BM算法一样其移动步长=匹配串中最右端的该字符到末尾的距离+1。
代码如下:
/*
Sunday-字符串匹配算法--一种优于KMP的算法
思想类似于BM算法,只不过是从左向右匹配
遇到不匹配的看大串中匹配范围之外的右侧第一个字符在小串中的最右位置
另外:采用BM/KMP的预处理的做法,事先计算好移动步长,等到遇到不匹配的值直接使用
*/
#include<iostream>
#include<string.h>
using namespace std;
//一个字符8位 最大256种
#define MAX_CHAR_SIZE 256
/*设定每个字符最右移动步长,保存每个字符的移动步长
如果大串中匹配字符的右侧一个字符没在子串中,大串移动步长=整个串的距离+1
如果大串中匹配范围内的右侧一个字符在子串中,大串移动距离=子串长度-这个字符在子串中的位置
*/
int *setCharStep(char *subStr)
{
int *charStep=new int[MAX_CHAR_SIZE];
int subStrLen=strlen(subStr);
for(int i=0;i<MAX_CHAR_SIZE;i++)
charStep[i]=subStrLen+1;
//从左向右扫描一遍 保存子串中每个字符所需移动步长
for(int i=0;i<subStrLen;i++)
{
charStep[(unsigned char)subStr[i]]=subStrLen-i;
}
return charStep;
}
/*
算法核心思想,从左向右匹配,遇到不匹配的看大串中匹配范围之外的右侧第一个字符在小串中的最右位置
根据事先计算好的移动步长移动大串指针,直到匹配
*/
int sundaySearch(char *mainStr,char *subStr,int *charStep)
{
int mainStrLen=strlen(mainStr);
int subStrLen=strlen(subStr);
int main_i=0;
int sub_j=0;
while(main_i<mainStrLen)
{
//保存大串每次开始匹配的起始位置,便于移动指针
int tem=main_i;
while(sub_j<subStrLen)
{
if(mainStr[main_i] == subStr[sub_j])
{
main_i++;
sub_j++;
continue;
}
else{
//如果匹配范围外已经找不到右侧第一个字符,则匹配失败
if(tem+subStrLen > mainStrLen)
return -1;
//否则 移动步长 重新匹配
char firstRightChar=mainStr[tem+subStrLen];
main_i =tem + charStep[(unsigned char)firstRightChar];
sub_j=0;
break;//退出本次失败匹配 重新一轮匹配
}
}
if(sub_j == subStrLen)
return main_i-subStrLen;
}
return -1;
}
int main()
{
char *mainStr="absaddsasfasdfasdf";
char *subStr="dd";
int *charStep=setCharStep(subStr);
cout<<"位置:"<<sundaySearch(mainStr,subStr,charStep)<<endl;
system("pause");
return 0;
}
|
|
|
/*************************************************华丽的分割线***************************************/
算法介绍以及实现伪码:http://www-igm.univ-mlv.fr/~lecroq/string/node19.html
void preQsBc(char *x, int m, int qsBc[]) {
int i;
for (i = 0; i < ASIZE; ++i)
qsBc[i] = m + 1;
for (i = 0; i < m; ++i)
qsBc[x[i]] = m - i;
}
void QS(char *x, int m, char *y, int n) {
int j, qsBc[ASIZE];
/* Preprocessing */
preQsBc(x, m, qsBc);
/* Searching */
j = 0;
while (j <= n - m) {
if (memcmp(x, y + j, m) == 0)
OUTPUT(j);
j += qsBc[y[j + m]]; /* shift */
}
}
|
|
// 第三个代码实现,貌似比较高效
http://hi.baidu.com/azuryy/blog/item/10d3d3460b97af0e6b63e5cd.html
头文件定义:
/* Sunday.h */
class Sunday
{
public:
Sunday();
~Sunday();
public:
int find(const char* pattern, const char* text);
private:
void preCompute(const char* pattern);
private:
//Let's assume all characters are all ASCII
static const int ASSIZE = 128;
int _td[ASSIZE] ;
int _patLength;
int _textLength;
};
源文件
/* Sunday.cpp */
Sunday::Sunday()
{
}
Sunday::~Sunday()
{
}
void Sunday::preCompute(const char* pattern)
{
for(int i = 0; i < ASSIZE; i++ )
_td[i] = _patLength + 1;
const char* p;
for ( p = pattern; *p; p++)
_td[*p] = _patLength - (p - pattern);
}
int Sunday::find(const char* pattern, const char* text)
{
_patLength = strlen( pattern );
_textLength = strlen( text );
if ( _patLength <= 0 || _textLength <= 0)
return -1;
preCompute( pattern );
const char *t, *p, *tx = text;
while (tx + _patLength <= text + _textLength)
{
for (p = pattern, t = tx; *p; ++p, ++t)
{
if (*p != *t)
break;
}
if (*p == 0)
return tx-text;
tx += _td[tx[_patLength]];
}
return -1;
}
简单测试下:
int main()
{
char* text = "blog.csdn,blog.net";
char* pattern = "csdn,blog" ;
Sunday sunday;
printf("The First Occurence at: %d/n",sunday.find(pattern,text));
return 1;
}
////////////////////////////////////////////
strstr的实现。
需要说明的是strstr是c语言提供的使用Brute Force实现的字符串匹配,简单、通用是其最大的优点。时间复杂度是O(mn)
// 下面是Microsoft的实现
//经典算法
//比KMP算法简单,没有KMP算法高效
char * __cdecl strstr (
const char * str1,
const char * str2
)
{
char *cp = (char *) str1;
char *s1, *s2;
if ( !*str2 )
return((char *)str1);
while (*cp)
{
s1 = cp;
s2 = (char *) str2;
while ( *s1 && *s2 && !(*s1-*s2) )
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/whoismickey/archive/2009/02/08/3869367.aspx
strstr
glibc里的strstr函数用的是brute-force(naive)算法,它与其它算法的区别是strstr不对pattern(needle)进行预处理,所以用起来很方便。理论复杂度O
将两种思想结合起来,可以做出更快的strstr(3)。约定(1) 为strstrBerg; (2) 为strstrBergo,(3)为lstrstr,(4)为glibc中的strstr,简单测试了一下:
从长度为2k的文本中查找长度为1、2、9的模式串,结果如下
1 2 9
(1)0.000006 0.000006 0.000012
(2)0.000007 0.000004 0.000008
(3)0.000002 0.000002 0.000005
(4)0.000005 0.000005 0.000011
下载strstr和测试程序,
下载后执行 :
unzip testStrstr.zip
cd testStrstr
make test
分享到:
相关推荐
### 单字符串匹配算法总结 #### 一、引言 字符串匹配是计算机科学中的一个重要问题,涉及在一段文本(通常称为“主串”或“文本串”)中查找特定的子串(通常称为“模式串”)。这类问题广泛应用于文本搜索、数据...
**KMP字符串匹配算法详解** ...总结,KMP字符串匹配算法通过构建部分匹配表来优化比较过程,减少了不必要的回溯,提升了搜索效率。在实际编程中,理解并运用KMP算法能够有效地解决字符串匹配问题,提高程序性能。
### 字符串匹配算法小集解析 #### 一、引言 本文档提供了一系列共35种不同的字符串匹配算法,这些算法在英文环境下被广泛讨论和应用。通过对这些算法进行详细的解析,我们可以更好地理解每种算法的特点、应用场景...
### 字符串匹配算法之BNDM:结合位并行与后缀自动机的高效灵活匹配 在计算机科学领域,字符串匹配算法是处理文本搜索、数据挖掘和生物信息学等应用中的关键工具。《字符串匹配算法之BNDM》一文深入探讨了一种创新的...
KMP算法是一种改进后的线性时间复杂度的字符串匹配算法。它通过预处理模式串生成next数组来避免重复比较,从而提高效率。时间复杂度为O(n+m)。 #### 2.3 Boyer-Moore算法 Boyer-Moore算法利用了两个重要的特性:坏...
KMP算法是一种高效的字符串匹配算法,它对基本的BF算法进行了优化。对于给定的原始串`S`和模式串`T`,目标是找到`T`在`S`中的位置。 **BF算法**(Brute-Force Algorithm,暴力匹配算法)的时间复杂度通常为`O...
在这个话题中,我们将深入探讨几种经典的字符串匹配算法,包括Bad Character Heuristic(BM算法)和Knuth-Morris-Pratt(KMP算法)。 **Bad Character Heuristic (BM算法)** BM算法是由Sunday和Sunday在1977年提出...
### 字符串匹配算法概述 在计算机科学领域中,字符串匹配是一种常见的操作,它涉及到在一个较大的文本串(T)中查找一个较短的模式串(P)。本章节将重点介绍字符串匹配的基本概念以及两种重要的算法——简单字符串...
Sunday算法提供了一种高效的方法来解决子字符串匹配问题。通过对模式字符串进行预处理并利用特定的移动策略,它能够在很多情况下优于Boyer-Moore算法和其他经典算法。在实际应用中,Sunday算法尤其适用于英语文本的...
### 字符串匹配算法概述 #### 1. 引言 在信息学竞赛与实际应用领域中,字符串处理占据着极其重要的地位。虽然表面上看似简单的字符串处理问题,却蕴含着丰富的算法思想和技术挑战。本篇文章将详细介绍由ACM金牌...
本实验“基于字符串模式匹配算法的病毒感染检测问题”聚焦于如何利用这些算法来识别潜在的恶意代码,从而预防计算机病毒感染。 《数据结构(C语言版 第2版)》一书由著名计算机科学家严蔚敏编著,书中涵盖了各种...
总结来说,CUDA并行实现字符串匹配是一项技术挑战,它涉及理解CUDA编程模型、并行算法设计、内存管理和性能优化等多个方面。通过巧妙地运用这些知识,开发者能够构建出高效且可扩展的解决方案,处理大规模的字符串...
总结来说,字符串匹配算法是计算机科学中的基础工具,BF和KMP算法是这一领域的经典代表。理解并掌握这些算法,不仅可以帮助优化搜索引擎的性能,还能为其他相关领域的算法设计提供灵感。随着技术的发展,还出现了更...
**KMP字符串模式匹配算法详解** KMP(Knuth-Morris-Pratt)算法是一种高效地在主串(text)中查找子串(pattern)的字符串模式匹配算法,由...通过理解其原理并结合实际问题,可以选择最适合的字符串匹配算法。
总结起来,朴素的字符串匹配算法(BF算法)是字符串处理中最基础的单模式匹配方法,虽然其效率较低,但在理解和实现上具有直观性和简洁性。随着技术的发展,人们不断探索和改进,衍生出更多高效算法,以满足各种复杂...
### KMP字符串匹配算法及其C语言实现 #### KMP算法简介 KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,由Donald Knuth、James H. Morris和Vaughan Pratt共同提出。该算法的核心思想是在模式串(需要...
字符串匹配算法的发展见证了计算机科学领域的不断进步,从简单的BF算法到高效的BM算法和WM算法,每一步都体现了研究人员对算法性能的不懈追求。BM算法以其独特的后向匹配策略和跳跃机制,在单模式字符串匹配中树立了...
总结来说,KMP算法是字符串匹配算法的一种优化,通过部分匹配表避免了冗余的比较,提高了效率,尤其适用于处理较长的模式串。掌握并理解KMP算法,对于提升算法设计和问题解决能力具有重要意义。
传统的字符串匹配算法,如KMP算法、Boyer-Moore算法等,都是基于精确匹配的原则进行设计的,即要求模式与主字符串中的部分完全相同才能认定为匹配成功。然而,在实际应用中,由于输入数据可能存在噪声、错误或变异,...