KMP并非效率最高的算法,实际采用并很少。各类文本编辑器的"查找"功能(Ctrl+F),大多采用Boyer-Moore算法。html
Boyer-Moore算法不只效率高,并且构思巧妙,容易理解。1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了这种算法。算法
下面,我根据Moore教授本身的例子来解释这种算法。编辑器
1.ide
假定字符串为"HERE IS A SIMPLE EXAMPLE",搜索词为"EXAMPLE"。this
2.idea
首先,"字符串"与"搜索词"头部对齐,从尾部开始比较。htm
这是一个很聪明的想法,由于若是尾部字符不匹配,那么只要一次比较,就能够知道前7个字符(总体上)确定不是要找的结果。ip
咱们看到,"S"与"E"不匹配。这时,"S"就被称为"坏字符"(bad character),即不匹配的字符。咱们还发现,"S"不包含在搜索词"EXAMPLE"之中,这意味着能够把搜索词直接移到"S"的后一位。字符串
3.get
依然从尾部开始比较,发现"P"与"E"不匹配,因此"P"是"坏字符"。可是,"P"包含在搜索词"EXAMPLE"之中。因此,将搜索词后移两位,两个"P"对齐。
4.
咱们由此总结出"坏字符规则":
后移位数 = 坏字符的位置 - 搜索词中的上一次出现位置
若是"坏字符"不包含在搜索词之中,则上一次出现位置为 -1。
以"P"为例,它做为"坏字符",出如今搜索词的第6位(从0开始编号),在搜索词中的上一次出现位置为4,因此后移 6 - 4 = 2位。再之前面第二步的"S"为例,它出如今第6位,上一次出现位置是 -1(即未出现),则整个搜索词后移 6 - (-1) = 7位。
5.
依然从尾部开始比较,"E"与"E"匹配。
6.
比较前面一位,"LE"与"LE"匹配。
7.
比较前面一位,"PLE"与"PLE"匹配。
8.
比较前面一位,"MPLE"与"MPLE"匹配。咱们把这种状况称为"好后缀"(good suffix),即全部尾部匹配的字符串。注意,"MPLE"、"PLE"、"LE"、"E"都是好后缀。
9.
比较前一位,发现"I"与"A"不匹配。因此,"I"是"坏字符"。
10.
根据"坏字符规则",此时搜索词应该后移 2 - (-1)= 3 位。问题是,此时有没有更好的移法?
11.
咱们知道,此时存在"好后缀"。因此,能够采用"好后缀规则":
后移位数 = 好后缀的位置 - 搜索词中的上一次出现位置
举例来讲,若是字符串"ABCDAB"的后一个"AB"是"好后缀"。那么它的位置是5(从0开始计算,取最后的"B"的值),在"搜索词中的上一次出现位置"是1(第一个"B"的位置),因此后移 5 - 1 = 4位,前一个"AB"移到后一个"AB"的位置。
再举一个例子,若是字符串"ABCDEF"的"EF"是好后缀,则"EF"的位置是5 ,上一次出现的位置是 -1(即未出现),因此后移 5 - (-1) = 6位,即整个字符串移到"F"的后一位。
这个规则有三个注意点:
(1)"好后缀"的位置以最后一个字符为准。假定"ABCDEF"的"EF"是好后缀,则它的位置以"F"为准,即5(从0开始计算)。
(2)若是"好后缀"在搜索词中只出现一次,则它的上一次出现位置为 -1。好比,"EF"在"ABCDEF"之中只出现一次,则它的上一次出现位置为-1(即未出现)。
(3)若是"好后缀"有多个,则除了最长的那个"好后缀",其余"好后缀"的上一次出现位置必须在头部。好比,假定"BABCDAB"的"好后 缀"是"DAB"、"AB"、"B",请问这时"好后缀"的上一次出现位置是什么?回答是,此时采用的好后缀是"B",它的上一次出现位置是头部,即第0 位。这个规则也能够这样表达:若是最长的那个"好后缀"只出现一次,则能够把搜索词改写成以下形式进行位置计算"(DA)BABCDAB",即虚拟加入最 前面的"DA"。
回到上文的这个例子。此时,全部的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出如今头部,因此后移 6 - 0 = 6位。
12.
能够看到,"坏字符规则"只能移3位,"好后缀规则"能够移6位。因此,Boyer-Moore算法的基本思想是,每次后移这两个规则之中的较大值。
更巧妙的是,这两个规则的移动位数,只与搜索词有关,与原字符串无关。所以,能够预先计算生成《坏字符规则表》和《好后缀规则表》。使用时,只要查表比较一下就能够了。
13.
继续从尾部开始比较,"P"与"E"不匹配,所以"P"是"坏字符"。根据"坏字符规则",后移 6 - 4 = 2位。
14.
从尾部开始逐位比较,发现所有匹配,因而搜索结束。若是还要继续查找(即找出所有匹配),则根据"好后缀规则",后移 6 - 0 = 6位,即头部的"E"移到尾部的"E"的位置。
代码以下:
package Boyer_Moore;
public class bmtest
{
/**
* TODO
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
BoyerMoore boyerMoore=new BoyerMoore();
int index=boyerMoore.Match("acbcdbceabc","abcd");
System.out.println(index);
}
}
package Boyer_Moore;public class BoyerMoore{ public int startindex; public int Match(String data,String key) { int datalength=data.length(); int keylength=key.length(); this.startindex=keylength-1; String suffixString=""; boolean matchflag=true; for(int i=startindex,j=keylength-1;j>-1;i--,j--) { if(key.charAt(j)==data.charAt(i)) { suffixString=key.charAt(j)+suffixString; continue; } else { matchflag=false; int badnumber=Badindex(data.charAt(i),j,key); int goodnumber=0; if(suffixString.length()>0){ goodnumber=Goodindex(data,suffixString,j,key); } int yiwei=goodnumber>badnumber?goodnumber:badnumber; startindex=startindex+yiwei; suffixString=""; if(startindex>=datalength) break; matchflag=true; j=keylength; i=startindex+1; } } if(matchflag) return startindex-keylength+1; else { return -1; } } private int Badindex(char badchar,int index,String key) { int charindex=key.indexOf(badchar); return index-charindex; } private int Goodindex(String data,String suffiString,int index,String key) { if(index<suffiString.length()) return key.length()-1; int goodindex=this.Match(data.substring(0,index), suffiString); if(goodindex<0) { return key.length()-1; } else { return key.length()-goodindex+suffiString.length(); } } }