算法——字符串匹配之BM算法
前言
Boyer-Moore算法是一种基于后缀匹配的模式串匹配算法(简称BM算法),后缀匹配就是模式串从右到左開始比較,但模式串的移动依旧是从左到右的。在实践中。BM算法效率高于前面介绍的《KMP算法》,算法分为两个阶段:预处理阶段和搜索阶段;预处理阶段时间和空间复杂度都是是O(m+sigma),sigma是字符集大小。一般为256。在最坏的情况下算法时间复杂度是O(m*n);在最好的情况下达到O(n/m)。
BM算法实现
BM算法预处理过程
BM算法有两个规则分别为坏字符规则(Bad
Character Heuristic)和好后缀规则(Good Suffix Heuristic)。这两种规则目的就是让模式串每次向右移动尽可能大的距离。BM算法是每次向右移动模式串的距离是,依照好后缀算法和坏字符算法计算得到的最大值。下面给出基本概念:
坏字符:输入文本字符串中的字符与模式串当前字符不匹配时,则文本字符串的该字符称为坏字符;
好后缀:是指在遇到坏字符之前,文本串和模式串已匹配成功的字符子串;
以下是坏字符和好后缀的图示:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hlbmhhbnpodW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
坏字符规则:当输入文本字符串中的某个字符跟模式串的某个字符不匹配时。模式串须要向右移动以便进行下一次匹配,移动的位数
= 坏字符在模式串中相应的位置 - 坏字符在模式串中最右出现的位置。此外,假设模式串中不存在"坏字符"。则最右出现位置为-1;所以坏字符规则必定有两种情况。以下会进行讨论。
好后缀规则:当字符失配时,后移位数 = 好后缀在模式串中相应的位置 - 好后缀在模式串上一次出现的位置,且假设好后缀在模式串中没有再次出现,则为-1。
依据模式串是否存在好后缀或部分好后缀,能够分为三种情况,以下会逐一讨论。
坏字符规则
坏字符规则有两种情况,例如以下图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hlbmhhbnpodW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
好后缀规则
若文本字符串和模式串匹配了一个好后缀u, 以下依据模式串其它位置是否存在好后缀进行不同的移动。假如,模式串pat的后u个字符和文本串txt都已经匹配了。可是下一个字符是坏字符,则须要移动模式串又一次匹配。若在模式中依旧存在同样的后缀或部分后缀,
那把最长的后缀或部分后缀移动到当前后缀位置。若模式串pat不存在其它的好后缀,则直接右移整个pat。因此好后缀规则有三种情况,例如以下图所看到的:
好后缀规则和坏字符规则的大小通过模式串的预处理数组的简单计算得到。
坏字符算法的预处理数组是bmBc[]。好后缀算法的预处理数组是bmGs[]。
计算坏字符数组bmBc[]
Case1:若模式串存在坏字符,若模式串存在多个坏字符时,选取最右边的那个字符。bmBc['b']表示字符b在模式串中最右出现的位置。
比如以下模式串中出现坏字符b的位置分别为j,k,i;则选取最右位置i作为bmBc['b']的值。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hlbmhhbnpodW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="font-family:KaiTi_GB2312; line-height:25.2000007629395px; font-size:18px">
Case2:字符在模式串中没有出现。如模式串中没有字符b,则bmBc['b']
= -1。
坏字符数组bmBc[]源代码实现例如以下:
void PreBmBc(const string &pat, int m, int bmBc[])
{
int i = 0;
// Initialize all occurrences as -1, include case2
for(i = 0; i < MAX_CHAR; i++)
bmBc[i] = -1;
// case1:Fill the actual value of last occurrence of a character
for(i = 0; i < m; i++)
bmBc[pat[i]] = i; }
计算好后缀数组bmGs[]
求解好后缀数组之前先求解好后缀数组长度的辅助数组suff[]; suff[i]=s" title="suff[i]=s" alt="">
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hlbmhhbnpodW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
suff[i]就是求pat中以i位置字符为后缀和以最后一个字符为后缀的公共后缀串的长度(包含当前位置字符)。以下举例说明:
i : 0 1 2 3 4 5 6 7
| | | | | | | |
pat: b c a b a b a b
/*
当i=m-1=7时,则suff[7]=8;
当i=6时。以pat[6]为后缀的后缀字符串为bcababa,以最后一字符b为后缀的后缀字符串为bcababab
则不存在公共最长子串,即suff[6]=0;
当i=5时,以pat[5]为后缀的后缀字符串为bcabab,以最后一字符b为后缀的后缀字符串为bcababab
则公共最长子串abab,即suff[5]=4;
当i=4时,以pat[4]为后缀的后缀字符串为bcaba,以最后一字符b为后缀的后缀字符串为bcababab
则不存在公共最长子串,即suff[4]=0;
.......
当i=0时,以pat[0]为后缀的后缀字符串为b,以最后一字符b为后缀的后缀字符串为bcababab
则公共最长子串b。即suff[0]=1;
*/
suff数组的定义:引用自《Boyer-Moore
algorithm》
对于;
当中m为模式串的长度。 latex=suff[m-1]=m"> suff[m-1]=m" title="suff[m-1]=m" alt="">
void suffix(const string &pat, int m, int suff[])
{
int i, j; suff[m - 1] = m; for(i = m - 2; i >= 0; i--)
{
j = i;
while(j >= 0 && pat[j] == pat[m - 1 - i + j]) j--; suff[i] = i - j;
}
}
有了上面求解的好后缀长度数组suff[]。如今能够计算好后缀数组bmGs[],依据前面好后缀的三种情况。这里求解数组也相应三种情况:
则能够写出好后缀数组bmGs[]的源码:
void PreBmGs(const string &pat, int m, int bmGs[])
{
int i, j;
int suff[SIZE]; // computed the suff[]
suffix(pat, m, suff); // Initialize all occurrences as -1, include case3
for(j = 0; j < m; j++)
{
bmGs[j] = -1;
} // Case2
j = 0;
for(i = m - 1; i >= 0; i--)
{
if(suff[i] == i + 1)
{
for(; j < m - 1 - i; j++)
{
if(bmGs[j] == -1)
bmGs[j] = i;
}
}
} // Case1
for(i = 0; i <= m - 2; i++)
{
j = m - 1 - suff[i];
bmGs[j] = i;
}
}
BM算法匹配过程
到此为止已经解说了BM算法的求解方法,下面给出BM算法的程序:
#include <iostream>
#include <string> using namespace std; const int MAX_CHAR = 256;
const int SIZE = 256;
static inline int MAX(int x, int y){return x < y ? y:x;} void BoyerMoore(const string &pat, const string &txt); int main()
{
string txt = "abababaacbabaa";
string pat = "babaa"; BoyerMoore(pat,txt);
system("pause");
return 0;
} void PreBmBc(const string &pat, int m, int bmBc[])
{
int i = 0;
// Initialize all occurrences as -1, include case2
for(i = 0; i < MAX_CHAR; i++)
bmBc[i] = -1;
// case1:Fill the actual value of last occurrence of a character
for(i = 0; i < m; i++)
bmBc[pat[i]] = i; } void suffix(const string &pat, int m, int suff[])
{
int i, j; suff[m - 1] = m;
for(i = m - 2; i >= 0; i--)
{
j = i;
while(j >= 0 && pat[j] == pat[m - 1 - i + j])
j--; suff[i] = i - j;
}
} void PreBmGs(const string &pat, int m, int bmGs[])
{
int i, j;
int suff[SIZE]; // computed the suff[]
suffix(pat, m, suff);
// Initialize all occurrences as -1, include case3
for(j = 0; j < m; j++)
bmGs[j] = -1;
// Case2
j = 0;
for(i = m - 1; i >= 0; i--)
{
if(suff[i] == i + 1)
{
for(; j < m - 1 - i; j++)
{
if(bmGs[j] == -1)
bmGs[j] = i;
}
}
} // Case1
for(i = 0; i <= m - 2; i++)
{
j = m - 1 - suff[i];
bmGs[j] = i;
}
} void BoyerMoore(const string &pat, const string &txt)
{
int j, bmBc[MAX_CHAR], bmGs[SIZE]; int m = pat.length();
int n = txt.length(); // Preprocessing
PreBmBc(pat, m, bmBc);
PreBmGs(pat, m, bmGs); // Searching
int s = 0;// s is shift of the pattern with respect to text
while(s <= n - m)
{
j = m - 1;
/* Keep reducing index j of pattern while characters of
pattern and text are matching at this shift s */
while(j >= 0 && pat[j] == txt[j + s])
j--; /* If the pattern is present at current shift, then index j
will become -1 after the above loop */
if(j < 0)
{
cout<<"pattern occurs at shift :"<< s<<endl;
/* Shift the pattern so that the next character in text
aligns with the last occurrence of it in pattern.
The condition s+m < n is necessary for the case when
pattern occurs at the end of text */
s += (s+m < n)? m-bmBc[txt[s+m]] : 1;
}
else
{/* Shift the pattern with the Max value between bmBc[] and bmGs[] */
s += MAX(j - bmBc[txt[s+j]], j-bmGs[j]);
}
}
}
參考资料:
http://www-igm.univ-mlv.fr/~lecroq/string/node14.html
http://blog.csdn.net/v_july_v/article/details/7041827
http://blog.jobbole.com/52830/
http://www.geeksforgeeks.org/pattern-searching-set-7-boyer-moore-algorithm-bad-character-heuristic/
http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html
算法——字符串匹配之BM算法的更多相关文章
- hrbustoj 1551:基础数据结构——字符串2 病毒II(字符串匹配,BM算法练习)
基础数据结构——字符串2 病毒IITime Limit: 1000 MS Memory Limit: 10240 KTotal Submit: 284(138 users) Total Accepte ...
- HDU 1711 Number Sequence (字符串匹配,KMP算法)
HDU 1711 Number Sequence (字符串匹配,KMP算法) Description Given two sequences of numbers : a1, a2, ...... , ...
- 字符串匹配的sunday算法
sunday算法核心思想:启发式移动搜索步长! SUNDAY 算法描述: 字符串查找算法中,最著名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore).这里介 ...
- 实现字符串匹配的KMP算法
KMP算法是Knuth-Morris-Pratt算法的简称,它主要用于解决在一个长字符串S中匹配一个较短字符串s. 首先我们从整体来把我这个算法的思想. 字符串匹配的朴素算法: 我们容易想到朴素算法, ...
- Luogu 3375 【模板】KMP字符串匹配(KMP算法)
Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...
- 字符串匹配的 Boyer-Moore 算法
上一篇文章,我介绍了 字符串匹配的KMP算法 但是,它并不是效率最高的算法,实际采用并不多.各种文本编辑器的” 查找” 功能(Ctrl+F),大多采用 Boyer-Moore 算法. 下面,我根据 M ...
- 字符串匹配的 KMP算法
一般字符串匹配过程 KMP算法是字符串匹配算法的一种改进版,一般的字符串匹配算法是:从主串(目标字符串)和模式串(待匹配字符串)的第一个字符开始比较,如果相等则继续匹配下一个字符, 如果不相等则从主串 ...
- 字符串匹配的kmp算法 及 python实现
一:背景 给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题. Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的常 ...
- 字符串匹配(KMP 算法 含代码)
主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知 ...
随机推荐
- 命令行下修改postgres密码
1. 修改PostgreSQL数据库默认用户postgres的密码 PostgreSQL数据库创建一个postgres用户作为数据库的管理员,密码随机,所以需要修改密码,方式如下: 步骤一:登录Pos ...
- python常用方法总结
1.os模块的路径拼接: import os now_path=os.path.abspath(__file__)#当前运行文件的路径 print(now_path) uppeer_path=os.p ...
- 如何在 Rails 中搭配 Turbolinks 使用 Vue
[Rails] Vue-outlet for Turbolinks 在踩了 Rails + Turbolinks + Vue 的許多坑後,整理 的作法並和大家分享. Initialize the A ...
- linux下 export只能设定临时变量
今天在调用ABBYY API的时候,需要传递APPID和APPPASSWD给系统环境才能够执行相应的python调用代码. 设置之后,因为写代码自己关掉了terminal,后面直接运行报错,访问权限不 ...
- maven无法下载依赖jar包—几种仓库的区别
一.问题背景 最近这两天,感觉自己智商急剧退化,到了自己都捉急的地步,呃,有必要记录下来,以后智商被人甩几条街的时候,看看这篇文字,找找灵感也是好的! 这个项目呢,是用IDEA开发的,我一切都弄好了, ...
- curl 设置头部
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ...
- 【Luogu】P3971Alice And Bob(贪心)
题目链接 容易发现值为x的点只可能从值为x-1的点转移过来,所以我们把原序列连成一棵树,dfs序就是原序列的一种形式. 就可以直接求啦 #include<cstdio> #include& ...
- [暑假集训--数论]poj2657 Comfort
Description A game-board consists of N fields placed around a circle. Fields are successively number ...
- hdu 2262 高斯消元求期望
Where is the canteen Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Ot ...
- vue.js源码学习分享(一)
今天看了vue.js源码 发现非常不错,想一边看一遍写博客和大家分享 /** * Convert a value to a string that is actually rendered. *转换 ...