字串匹配演算法(二)窮舉與自動機

2021-08-22 20:06:00 字數 1579 閱讀 3976

窮舉法又叫暴力法。大多數程式設計師眼裡,它是幼稚的,但大師們不這麼認為。

窮舉法用在字串匹配上,簡單的描述就是,檢查文字從0到n-m的每乙個位置,看看從這個位置開始是否與模式匹配。這種方法還是有一些優點的,如:不需要預處理過程,需要的額外空間為常數,每一趟比較時可以以任意順序進行。

儘管它的時間複雜度為o(mn),例如在文字"aaaaaaaaaaaaaaaaaaaaaaaaaaa"中尋找"aaaaab"時,就完全體現出來了。但是演算法的期望值卻是2n,這表明該演算法在實際應用中效率不低。

c**如下:

voidbf(char*x,intm,char*y,intn)

}如果我們注意到c庫函式是彙編優化過的,並通常能提供比c**更高的效能的話,我們可以用memcmp來完成每一趟比較過程,從而達到更好的效能:

#defineeos'\0'

voidbf(char*x,intm,char*y,intn)

自動機的方法其實和窮舉法有點相似,都是用最簡單直白的方式來做事情。區別在於窮舉法是在計算,而自動機則是查表。儘管自動機的構造過程有一點點難解,要涉及到dfa的理論,但是自動機的比較過程那絕對是簡單到無語。

簡單說來,根據模式串,畫好了一張大的**,**m+1行σ列,這裡σ表示字母表的大小。**每一行表示一種狀態,狀態數比模式長度多1。一開始的狀態是0,也就是處在**的第0行,這一行的每個元素指示了當遇到某字元時就跳轉到另乙個狀態。每當跳轉到最終狀態時,表示找到了乙個匹配。

語言表述起來還是比較囉嗦,看**就知道了:

#defineasize256

intpreaut(const

char*x,intm,int*aut)

returnstate;

}voidaut(const

char*x,intm,const

char*y,intn)}

從**上我們很容易看出,自動機的構造需要時間是o(mσ),空間也是o(mσ)(嚴格來說這份**使用了o((m+1)σ)),但是一旦構造完畢,接下來匹配的時間則是o(n)。

匹配的過程前面已經說了,太簡單了沒什麼好說的,這裡就解釋一下構造過程吧!

我們構造的目標是對應模式長度,構造出同樣多的狀態,用0表示初始狀態,然後第乙個字元用狀態1表示,第二個用狀態2表示,依次類推,直到最後乙個字元,用m表示,也是最終狀態。

一開始,陣列全都置0,,這個時候的自動機遇到任何字元都轉到初始狀態。然後給它看模式的第乙個字元,假設這是'a'吧,告訴它,狀態0遇到'a'應該到乙個新的狀態——狀態1,所以把第0行的第'a'列修改為1。而這個時候狀態1還是空白的,怎麼辦呢?

這時候狀態0就想呀,在我被告知遇到'a'要去狀態1之前,我原本遇到'a'都要去狀態0的,也就是修改之前第'a'列所指的那個狀態,稱為old狀態吧;而現在我遇到'a'卻要去乙個新的狀態,既然以前old狀態能處理遇到'a'之後的事情,那麼我讓新的狀態像old狀態一樣就好了。於是狀態0把old狀態拷貝到狀態1。

現在輪到狀態1了,給它看第二個字元,它也如法炮製,指向了狀態2,又把old狀態拷貝給了狀態2。

於是,狀態機就在這種代代傳承的過程中構造完畢了。

雖然理論上自動機是最完美的匹配方式,但是由於預處理的消耗過大,實踐中,主要還是用於正規表示式。

字串匹配演算法(二)窮舉與自動機

窮舉法又叫暴力法。大多數程式設計師眼裡,它是幼稚的,但大師們不這麼認為。窮舉法用在字串匹配上,簡單的描述就是,檢查文字從0到n m的每乙個位置,看看從這個位置開始是否與模式匹配。這種方法還是有一些優點的,如 不需要預處理過程,需要的額外空間為常數,每一趟比較時可以以任意順序進行。儘管它的時間複雜度為...

字串匹配 有限自動機

有限自動機包含乙個五元組 q,q0,a,q表示狀態有限集 q0為初始狀態 q0 q a表示接受狀態的集合 a 是q的子集 是輸入字母表 是狀態轉移函式 對於終態函式我還不是很理解,希望大家不吝賜教 演算法導論上有乙個例子 p ababaca 那麼轉換成五元組是 q q0 0 注 0狀態什麼都沒有 a...

字串匹配演算法 利用有限自動機進行匹配

本文內容與 演算法導論 中字串匹配章節相關並部分摘錄。常用的字串匹配演算法有樸素字串匹配演算法,rabin karp演算法,利用有限自動機進行字串匹配和kmp演算法等。前面兩種比較簡單,重點是後面兩種。利用有限自動機進行字串匹配 假設要對文字字串t進行掃瞄,找出模式p的所有出現位置。這個方法可以通過...