参考:
http://blog.csdn.net/WINCOL/article/details/4795369
http://blog.csdn.net/v_JULY_v/article/details/6545192
总结:
1) 基本思想:
BF算法是最简单效率最低的算法。采用逐字符比较再回退。
KMP, Hospool, BM 以及Sunday算法采用一个相同的基本思想就是分析模式串P的自身特征,并将此特征存储在一个特征数组中,在进行匹配时可以根据此特征数组减少重复的比较,提高匹配速度。
RK算法将整个长度为M的字符串看做一个整体,针对其计算出一个特征值。仅在整体特征值相同时,才进行进一步的逐字符的比较。其实际上是将匹配分成两个阶段,第一阶段保证近似相同,第二阶段保证完全相同。
2) 算法效率:
BF算法有最低的效率,最好最坏平均复杂度都为O(M*N)
KMP和BM有理论上最坏O(M*N)的运行效率。
RK算法平均复杂度接近O(M+N),最坏复杂度为O(MN)
Sunday最坏复杂度为O(MN)
在实际使用中,BM将比KMP快几倍
Sunday算法比BM还要快
1, Brute Force(BF或蛮力搜索) 算法:
首先将匹配串和模式串左对齐,然后从左向右一个一个进行比较,如果不成功则模式串向右移动一个单位。O(M*N)复杂度
2, KMP算法:
思想:存储模式串自身的特征信息以减少重复比较。
算法复杂度为O(M+N)
特征数组:next[i] 存储模式串中i前边的字符串最大的相等前缀和后缀
若S="abcabecabb", P="abcabb"
第一轮比较:
匹配串:abcabecabb
模式串:abcabb
根据已计算好的next[5]=2, 下次比较可将P串向后移动5-2=3个位置
第二轮比较:
匹配串:abcabecabb
模式串: abcabb
代码://代码4-1
//修正后的求next数组各值的函数代码
void get_nextval(char const* ptrn, int plen, int* nextval)
{
int i = 0;
nextval[i] = -1;
int j = -1;
while( i < plen-1 )
{
if( j == -1 || ptrn[i] == ptrn[j] ) //循环的if部分
{
++i;
++j;
//修正的地方就发生下面这4行
if( ptrn[i] != ptrn[j] ) //++i,++j之后,再次判断ptrn[i]与ptrn[j]的关系
nextval[i] = j; //之前的错误解法就在于整个判断只有这一句。
else
nextval[i] = nextval[j];
}
else //循环的else部分
j = nextval[j];
}
}
//代码5-1
//int kmp_seach(char const*, int, char const*, int, int const*, int pos) KMP模式匹配函数
//输入:src, slen主串
//输入:patn, plen模式串
//输入:nextval KMP算法中的next函数值数组
int kmp_search(char const* src, int slen, char const* patn, int plen, int const* nextval, int pos)
{
int i = pos;
int j = 0;
while ( i < slen && j < plen )
{
if( j == -1 || src[i] == patn[j] )
{
++i;
++j;
}
else
{
j = nextval[j];
//当匹配失败的时候直接用p[j_next]与s[i]比较,
//下面阐述怎么求这个值,即匹配失效后下一次匹配的位置
}
}
if( j >= plen )
return i-plen;
else
return -1;
}
3,Horspool 算法
Horspool 算法的思想很简单的。不过有个创新之处就是模式串是从右向左进行比较的。
匹配串:abcbc sdxzcxx
模式串:cbcac
这个时候我们从右向左进行对暗号,c-c ,恩对上了,第二个b-a ,不对啊,我们应该怎么办?难道就这么放弃么。于是,模式串从不匹配的那个字符开始从右向左寻找匹配串中不匹配的字符b 的位置,结果发现居然有,赶快对上赶快对上,别耽误了。
匹配串:ab cbcsd xzcxx
模式串: cbcac
然后继续从最右边的字符从右向左进行比较。这时候,我们发现了,d-c 不匹配啊,而且模式穿里面没有噢,没办法,只好移动一个模式串长度的单位了。
匹配串:abcbcsdxzcxx
模式串: cbcac
4,BM(Boyer-Moore) 算法
BM算法的特征数组有两个。匹配的顺序和hospool相同,有后向前匹配。BM算法理论上时间复杂度和KMP 差不多,但是实际上却比KMP 快数倍。
坏字符规则:与hosrool算法相同,Fa存储当前字符为不匹配字符时,模式串需要向后移动的长度。即Fa[i] = len(P) - i
最好后缀规则:Fb存储的值是一个位置,让已匹配过的后缀与模式串中从后往前最近的一个相同的子串对齐。
例如对于模式串cbacbadcba, "d"对应的Fb[6]=6-3+1=4 表示如果在“d”处未匹配,则需要将模式串后移4位。原因是模式串中离已匹配的“cba”最近的相同子串是位置3处的“cba”.故下次比较只需要将位置3处的“cba”与S中的"cba"对齐即可。
匹配串:abaccba bbazz
模式串:cbadcba
我们看到已经匹配好了cba ,但是c-d 不匹配,这个时候既可以采用坏字符规则,也可以使用最好后缀规则( 模式串:cba dcba ) ,在这种情况下,选择最好后缀规则(以为根据坏字符规则向后移动3个位置,而根据最好后缀规则移动4个位置)
匹配串:abaccbabbaz z
模式串: cbadcba
当已匹配好的部分在P中没有重复时,Fb中存储的位置应为P的长度,即向后移动P的长度。
匹配串:abacccb bbazzccffgd
模式串:cbadccb
然后得到
匹配串:abacccbbbazzccffgd
模式串: cbadccb
5, Sunday 算法
Sunday 的算法思想和Horspool 有些相似,但是, 当出现不匹配的时候,却不是去找匹配串中不匹配的字符在模式串的位置,而是直接找最右边对齐的右一位的那个字符在模式串的位置。
比如:
匹配串:abcbc zdxzc
模式串:zbcac
这里b-a 没有对上,那么查找匹配串中的z 在模式串的位置,然后,对齐z
匹配串:abcbczdxzc
模式串: zbcac
如果模式串中的没有那个字符则直接跳过
匹配串:abcbc edxzcs
模式串:zbcac
e 不在模式串中出现
那么我们就
匹配串:abcbce dxzcs
模式串: zbcac
6,RK(Rabin-Karp) 算法
基本思路:将长度为M的模式串作为一个整体计算出一个特征值。在匹配时,首先比较长度为m的子串的特征值是否与模式串相同。只有在相同时才进行进一步的逐字符的比较。
一种特征值计算方法:将m个字符的和作为特征值。
P=p[m] + p[m-1] + p[m-2] + .... + p[2] + p[1]
匹配串:abcbczbcac -->F(a..c) = f1
模式串:zbcac -->F(z..c) = f0
以为f1!=f0则进行下一步查找
匹配串:abcbczbcac-->F(b..d) = f1-a+z=f2
模式串: zbcac
以为f2!=f0则进行下一步查找
.......
匹配串:abcbczbcac -->F(b..d) = f1-a+d=f2
模式串: zbcac
以为f2==f0 则进一步逐字符判断是否相等
另一种特征值计算方法(原始RK算法采用的):将m个字符作为一个26进制数
P = p[m] + 10(p[m -1] + 10(p[m-2]+...+10(p[2]+10p[1])...))
7,其他算法
字符串匹配自动机
后缀字典树算法
非在线后缀树算法
在线后缀树算法
AC算法
WM算法