字串匹配問題

2021-04-13 21:42:10 字數 2811 閱讀 3148

在字串匹配問題中,我們期待察看串t中是否含有串p。

其中串t被稱為目標串,串s被稱為模式串。

進行字串匹配,最簡單的乙個想法是:

public

class

******match 

}可以看見,這個演算法(假定m>>n)的複雜度是o(mn),其中m是t的長度,n是p的長度。這種演算法的缺陷是匹配過程中帶有回溯——準確地說是t串存在回溯,也就是當匹配不成功的時候,之前進行的匹配完全變為無用功,所有的比較需要重新開始。

kmp演算法是d.e.knuth、j.h.morris和v.r.pratt提出的無回溯的字串匹配演算法,演算法的核心思想就是設法在匹配失敗的時候,盡量利用之前的匹配結果,消除t串的回溯問題。那麼如何消除回溯呢?請看下面的例子:

假設p=abacd,如果t=abax...,當從頭開始匹配到字元c時,若c=x,顯然,匹配過程繼續;當c≠x時,按照樸素的匹配演算法,t串會發生回溯,之後t串會從第2個字元b開始重新匹配,而不是從匹配失敗的字元x開始繼續。但是顯然,對於上述的匹配過程,t串不需要從b開始重新匹配,它只需要從x開始和p的b字元繼續匹配即可。如下:

匹配過程:

p=abacd

t=abax....

^----比較到此處時發生匹配失敗

樸素匹配演算法:

p= abacd

t=abax...

^----回溯到b,重新開始和p的匹配

kmp演算法:

p=  abacd

t=abax...

^----t串不回溯,從x處繼續匹配

現在的問題是,按照kmp演算法,匹配失敗的時候,p串需要重新調整位置,但是調整的依據是什麼?knuth等人發現,p調整位置的依據和p的構造有關,和t無關。具體來說,定義失效函式:f(j)=k,其中0<=k<=j,且k是使得p0p1...pk-1 = pj-k+1pj-k+2...pj成立的最大整數。建立失效函式的演算法如下:

public void build()

}匹配過程如下:

public int match(string target, int start) else

}if(pattern_index == pattern_length)

return target_index - pattern_length;

return -1;

}kmp演算法雖然能夠進行字串匹配,但是,在實踐中字串匹配往往還要支援萬用字元,ms系統中最常見的萬用字元是?和*。其中,?可以代表乙個字元(不能沒有),*可以代表任意多個字元(可以為空)。經典的kmp演算法針對萬用字元是無能為力的,但是經過簡單的改造,kmp演算法也可以識別萬用字元。

首先是?,根據?的功能,?表示任意字元,也就是說在匹配過程中,?永遠匹配成功。因此對匹配函式的修改十分簡單:

...while(target_index < token_length && pattern_index < pattern_length) else else if(pattern[i] == pattern[array[i - 1]] || pattern[i] == '?' || pattern[array[i - 1]] == '?')

array[i] = array[i - 1] + 1;

else

array[i] = begin;}} 

演算法中begin表示每段字串的開始位置。此外,匹配過程也應該進行相應的修改,因為字元*對於匹配沒有任何幫助,它屬於佔位符,因此需要跳過,匹配演算法如下:

public int match(string target, int start) else if(target[target_index] == pattern[pattern_index] || pattern[pattern_index] == '?') else

}if(pattern_index == pattern_length)

return target_index - pattern_length + begin;

return -1;

}乙個數字邏輯的問題:設計乙個識別11011的電路,解這個問題的關鍵就是設計出這個電路的dfa,如下:

仔細看看這個狀態機,是不是和kmp的演算法有幾分類似呢?這並不是巧合,因為kmp演算法中的失效函式總可以等價的轉化為乙個dfa。當然kmp的dfa遠比識別11011的dfa要複雜,原因在於kmp接受的輸入是全體字元集合,識別11011的dfa只接受0和1這兩個輸入。我們知道,乙個正則語言和乙個dfa是等價的,而kmp計算失效函式的演算法,實際上等價於求dfa的過程,f(j)的值實際上表明狀態j+1接受到不正確的字元時應該回溯到的狀態(注意此時輸入流並沒有前進)。普通的字串都能看成是乙個正則語言,含有萬用字元?和*的字串也可以等價的轉換為乙個正規表示式。但是,正則語言的集合遠比kmp演算法所能支援的模式集合的更大,期間原因還是剛才提過的輸入問題。試想p=p1p2...pn,當匹配到pj的時候,如果下乙個輸入字元正是pj,那麼狀態機進入下乙個狀態,如果不是pj,那麼狀態機按照實效函式的指示轉移到狀態f(j-1),也就是說kmp狀態機的每個狀態只能根據輸入是否為pj來進行轉移。而正規表示式所對應的狀態機則有所不同,如果正則語言l=l1l2...ln,假設這些都是字母,當匹配到lj位的時候,如果下乙個輸入字元正是lj,那麼狀態機進入下乙個狀態,否則它還可以根據輸入的值進行轉移,例如lj=c1時轉換到狀態x,lj=c2時狀態轉換到y等等。

字串匹配問題是老問題了,並沒有太多新意可言,只不過雖然kmp演算法十分簡單,但它的內在含義還是十分深刻的。橫向比較kmp、dfa和正則語言、正規表示式我們會發現,它們之間存在很多的關聯,而這種比較也有利於我們更好的理解這些演算法,或者改進這些演算法。最後說一句,試圖利用目前的框架使得kmp演算法支援全部種類的萬用字元(對應於正規表示式就是x?、x*、x+、等等)是不可能,而我們也不需要這麼做,因為我們還有正規表示式嘛。

字串匹配問題

給出乙個字串和多行文字,在這些文字中找到字串出現的那些行。你的程式還需支援大小寫敏感選項 當選項開啟時,表示同乙個字母的大寫和小寫看作不同的字元 當選項關閉時,表示同乙個字母的大寫和小寫看作相同的字元。輸入輸入的第一行包含乙個字串s,由大小寫英文本母組成。第二行包含乙個數字,表示大小寫敏感的選項,當...

字串匹配問題

字串中只含有括號 判斷輸入的字串中括號是否匹配。如果括號有互相包含的形式,從內到外必須是 例如。輸入 輸出 yes,而輸入 都應該輸出no。第一行為乙個整數n,表示以下有多少個由括好組成的字串。接下來的n行,每行都是乙個由括號組成的長度不超過255的字串 在輸出中有n行,每行都是yes或no。5 y...

字串匹配問題

字串匹配演算法 kmp和暴力求解。個人認為,在面試時,由於時間緊張,暴力求解未嘗不可以。kmp演算法,參考了 kmp演算法 if str k 1 str q 如果相同,k next q k 這個是把算的k的值 就是相同的最大字首和最大字尾長 賦給next q int kmp char str,int...