說起“字符串匹配”,恐怕算得上是計(jì)算機(jī)領(lǐng)域應(yīng)用最多的功能之一,為了滿足這一需求,聰明的計(jì)算機(jī)科學(xué)家們發(fā)明了許多巧妙的算法。在上一篇漫畫中,我們介紹了BF算法和RK算法,沒看過的小伙伴可以先補(bǔ)補(bǔ)...
說起“字符串匹配”,恐怕算得上是計(jì)算機(jī)領(lǐng)域應(yīng)用最多的功能之一,為了滿足這一需求,聰明的計(jì)算機(jī)科學(xué)家們發(fā)明了許多巧妙的算法。
在上一篇漫畫中,我們介紹了BF算法和RK算法,沒看過的小伙伴可以先補(bǔ)補(bǔ)課:
漫畫:什么是字符串匹配算法?
今天,我們來介紹一種性能大大優(yōu)化的字符串匹配算法。
BF算法是如何工作的?
正如同它的全稱BruteForce一樣,BF算法使用簡(jiǎn)單粗暴的方式,對(duì)主串和模式串進(jìn)行逐個(gè)字符的比較。
比如給定主串和模式串如下:
它們的比較過程是什么樣的呢?
第一輪,模式串和主串的第一個(gè)等長子串比較,發(fā)現(xiàn)第0位字符一致,第1位字符一致,第2位字符不一致:
第二輪,模式串向后挪動(dòng)一位,和主串的第二個(gè)等長子串比較,發(fā)現(xiàn)第0位字符不一致:
第三輪,模式串繼續(xù)向后挪動(dòng)一位,和主串的第三個(gè)等長子串比較,發(fā)現(xiàn)第0位字符不一致:
以此類推,一直到第N輪:
當(dāng)模式串挪動(dòng)到某個(gè)合適位置,逐個(gè)字符比較,發(fā)現(xiàn)每一位字符都是匹配時(shí),比較結(jié)束:
壞字符規(guī)則
“壞字符” 是什么意思?就是指模式串和子串當(dāng)中不匹配的字符。
還以上面的字符串為例,當(dāng)模式串和主串的第一個(gè)等長子串比較時(shí),子串的最后一個(gè)字符T就是壞字符:
當(dāng)檢測(cè)到第一個(gè)壞字符之后,我們有必要讓模式串一位一位向后挪動(dòng)和比較嗎?并不需要。
因?yàn)橹挥?strong>模式串與壞字符T對(duì)齊的位置也是字符T的情況下,兩者才有匹配的可能。
不難發(fā)現(xiàn),模式串的第1位字符也是T,這樣一來我們就可以對(duì)模式串做一次“乾坤大挪移”,直接把模式串當(dāng)中的字符T和主串的壞字符對(duì)齊,進(jìn)行下一輪的比較:
壞字符的位置越靠右,下一輪模式串的挪動(dòng)跨度就可能越長,節(jié)省的比較次數(shù)也就越多。這就是BM算法從右向左檢測(cè)的好處。
接下來,我們繼續(xù)逐個(gè)字符比較,發(fā)現(xiàn)右側(cè)的G、C、G都是一致的,但主串當(dāng)中的字符A,是又一個(gè)壞字符:
我們按照剛才的方式,找到模式串的第2位字符也是A,于是我們把模式串的字符A和主串中的壞字符對(duì)齊,進(jìn)行下一輪比較:
接下來,我們繼續(xù)逐個(gè)字符比較,這次發(fā)現(xiàn)全部字符都是匹配的,比較公正完成:
//在模式串中,查找index下標(biāo)之前的字符是否和壞字符匹配
private
static
int
findCharacter
(
String
pattern
,
char
badCharacter
,
int
index
)
{
for
(
int
i
=
index
-
1
;
i
>=
0
;
i
--){
if
(
pattern
.
charAt
(
i
)
==
badCharacter
){
return
i
;
}
}
//模式串不存在該字符,返回-1
return
-
1
;
}
public
static
int
boyerMoore
(
String
str
,
String
pattern
)
{
int
strLength
=
str
.
length
();
int
patternLength
=
pattern
.
length
();
//模式串的起始位置
int
start
=
0
;
while
(
start
<=
strLength
-
patternLength
)
{
int
i
;
//從后向前,逐個(gè)字符比較
for
(
i
=
patternLength
-
1
;
i
>=
0
;
i
--)
{
if
(
str
.
charAt
(
start
+
i
)
!=
pattern
.
charAt
(
i
))
//發(fā)現(xiàn)壞字符,跳出比較,i記錄了壞字符的位置
break
;
}
if
(
i
<
0
)
{
//匹配成功,返回第一次匹配的下標(biāo)位置
return
start
;
}
//尋找壞字符在模式串中的對(duì)應(yīng)
int
charIndex
=
findCharacter
(
pattern
,
str
.
charAt
(
start
+
i
),
i
);
//計(jì)算壞字符產(chǎn)生的位移
int
bcOffset
=
charIndex
>=
0
?
i
-
charIndex
:
i
+
1
;
start
+=
bcOffset
;
}
return
-
1
;
}
public
static
void
main
(
String
[]
args
)
{
String
str
=
"GTTATAGCTGGTAGCGGCGAA"
;
String
pattern
=
"GTAGCGGCG"
;
int
index
=
boyerMoore
(
str
,
pattern
);
System
.
out
.
println
(
"首次出現(xiàn)位置:"
+
index
);
}
好后綴規(guī)則
“好后綴” 又是什么意思?就是指模式串和子串當(dāng)中相匹配的后綴。
讓我們看一組新的例子:
對(duì)于上面的例子,如何我們繼續(xù)使用“壞字符規(guī)則”,會(huì)有怎樣的效果呢?
從后向前比對(duì)字符,我們發(fā)現(xiàn)后面三個(gè)字符都是匹配的,到了第四個(gè)字符的時(shí)候,發(fā)現(xiàn)壞字符G:
接下來我們?cè)谀J酱业搅藢?duì)應(yīng)的字符G,但是按照壞字符規(guī)則,模式串僅僅能夠向后挪動(dòng)一位:
這時(shí)候壞字符規(guī)則顯然并沒有起到作用,為了能真正減少比較次數(shù),輪到我們的好后綴規(guī)則出場(chǎng)了。由于好后綴規(guī)則的實(shí)現(xiàn)細(xì)節(jié)比壞字符規(guī)則要難理解得多,所以我們這里只介紹一個(gè)大概思路:
我們回到第一輪的比較過程,發(fā)現(xiàn)主串和模式串都有共同的后綴“GCG”,這就是所謂的“好后綴”。
如果模式串其他位置也包含與“GCG”相同的片段,那么我們就可以挪動(dòng)模式串,讓這個(gè)片段和好后綴對(duì)齊,進(jìn)行下一輪的比較:
顯然,在這個(gè)例子中,采用好后綴規(guī)則能夠讓模式串向后移動(dòng)更多位,節(jié)省了更多無謂的比較。
來源:本文內(nèi)容搜集或轉(zhuǎn)自各大網(wǎng)絡(luò)平臺(tái),并已注明來源、出處,如果轉(zhuǎn)載侵犯您的版權(quán)或非授權(quán)發(fā)布,請(qǐng)聯(lián)系小編,我們會(huì)及時(shí)審核處理。
聲明:江蘇教育黃頁對(duì)文中觀點(diǎn)保持中立,對(duì)所包含內(nèi)容的準(zhǔn)確性、可靠性或者完整性不提供任何明示或暗示的保證,不對(duì)文章觀點(diǎn)負(fù)責(zé),僅作分享之用,文章版權(quán)及插圖屬于原作者。
Copyright©2013-2025 ?JSedu114 All Rights Reserved. 江蘇教育信息綜合發(fā)布查詢平臺(tái)保留所有權(quán)利
蘇公網(wǎng)安備32010402000125
蘇ICP備14051488號(hào)-3技術(shù)支持:南京博盛藍(lán)睿網(wǎng)絡(luò)科技有限公司
南京思必達(dá)教育科技有限公司版權(quán)所有 百度統(tǒng)計(jì)