利用有限自動機(finite automata)進行模式匹配

1、1、有限自動機定義及基本術語:

一個有限自動機 M 是一個5元組(Q, q0,A, Σ, δ),其中:
ios

  • Q 是全部狀態的有限集合;算法

  • q0 ∈ Q (屬於)是初始狀態;函數

  • A ⊆ Q (子集)是接受狀態的集合;(對應於多模式?)測試

  • Σ 是有限輸入字母表;spa

  • δ 是從Q * Σ的轉移函數,稱爲有限自動機M的轉移函數;debug


記號與術語:
Σ*  表示用字母表Σ中全部字符造成的全部有限長度的字符串集合.
n   輸入字符串(input string)的長度.
m  模式字符串(pattern string)的長度;也稱做終態m,當狀態爲m時表示,m長度的模式串匹配成功.code

|x| : 字符串x的長度, 如示符號記法.遞歸

 : 字符串w 是字符串x 的前綴,如示符號記法.字符串

 : 字符串w 是字符串x 的後綴,如示符號記法.(注意前綴/後綴均遵循傳遞規則)input

ε:表示空字符串,是全部字符串的後綴,前綴. (ε讀做 epsilon )

a : 下文中的字符a泛指全部字符(a∈Σ),不特指字符'a'.


2、引入的函數定義:

  • 轉移函數δ(transition function) ( δ 讀做"delta",對應大寫爲 Δ )

        有限自動機開始於初始狀態q0,每次讀入輸入字符串的一個字符,若是有限自動機在狀態q是讀入字符'a', 
        則M狀態從q變成 δ(q, a);


  • 終態函數 Φ(finite state function) ( Φ 讀做"fai", 對應小寫爲 φ )

        是從 Σ* 到 Q 的函數,Φ(w)是永動機M 掃描字符串 w 終止後的狀態;M 接受字符串w 當且僅當Φ(w)∈A, 函數Φ有下列遞歸關係定義:
                φ(ε) = q0;(空字符串 ε 的終態爲q0)

                φ(wa) = δ(φ(w),a) (其中w∈Σ*,a∈Σ)


  • 輔助函數,後綴函數σ 對應於模式字串P  ( σ 讀做 "sigma", 對應大寫爲 Σ )

        是從Σ* 到{0,1, ..., m}上的映射,σ(x)是字符串x的後綴同時是P的前綴的最大長度;
                σ(x) = max{k: Pk ⊐ x }
                有P0 = ε是全部全部字符串的後綴; 

* 後綴函數的主要意義的是求出當前匹配失敗時,求出已經匹配過的部分字串x是不是待匹配模式字串P的前綴,即匹配能夠跳過x中部分長度( σ(x) ),能夠用於實現轉移過程;同時也代表在接受輸入字符串x後的狀態(終態),即也用於實現終態函數。


3、字符串匹配自動機(string-matching automation)

下圖是依據模式串 P="ababaca" 構建的自動機圖表:

 

上圖(a)是一個自動機的狀態轉換圖表,接受全部以字符串"ababaca"結尾的字符串。其中狀態0是初始狀態,狀態7是惟一接受狀態(單模式匹配).

1) 從狀態i到狀態j的帶箭頭的有向邊表示轉移過程: δ(i, a) = j(a∈Σ).

2) 右向邊組成了自動機的主要"骨架",圖中粗線部分,對應於輸入字符同模式字串匹配成功的轉移過程。左向邊對應於匹配失敗的轉移過程(跳轉,主要是計算已經匹配的部分字串 的後綴子串同時是模式串P的前綴的最大長度).部分匹配失敗的過程沒有標示出來。

3) 圖中部分狀態i在接受某字符a(a∈Σ)時,沒有標示出對應有向邊的狀況代表其轉移過程爲: δ(i, a) = 0(a∈Σ),根據下面字符串模式匹配自動機定義,知當前已經匹配子串沒有後綴字串是模式串P的前綴。如在狀態3時,輸入字符爲'c',即在已經匹配了"aba"這時接受字符'c',知當前已匹配字串爲"abac",對應模式字串P="ababaca",可知這時匹配失敗,進行失敗跳轉求"abac"後綴子串同時是模式串P前綴的最大長度,可知爲0.

4) 匹配成功的轉移過程(對應狀態,以及對應輸入字符)均標示爲灰色,

5) 表(c)是自動機在處理(接受)輸入文本T="abababacaba"的最終狀態表。當輸入字符T[i]時,此時字串T[0...i]對應的的最終狀態φ(T[0...i]) 同表(c)最後一列一一對應。有T["abababaca"] = P.length = 7(惟一接受狀態),即這時候在T串中匹配成功模式串P,結束位置爲9,起始位置爲(9-P.length+1)=3。


一、字符串匹配有限自動機定義:

給定模式(pattern)字符串 P[1...m],其對應的字符串匹配有限自動機定義以下:

一、狀態集Q = {0,1,...m},開始狀態q0 是狀態0,state m 是惟一的接受狀態;

二、轉移函數δ 能夠用後綴函數來表示 (這個很重要, 由於狀態轉移函數是個抽象概念,然後綴函數能夠用code表示) :

δ(q,a) = σ(Pq,a)   <等式一>


假設當前已經讀入的字符串爲T,爲了讓T的字串(以T[i]爲結尾) 能匹配模式字串Pj,必須知足Pj是Ti的後綴;同時假設q = φ(Ti),說明讀取字串Ti後自動機M 狀態變成q;同時根據轉移函數<等式一>可知q是模式字串P最大長度的前綴,同時是Ti的後綴;所以在狀態q,有Pq ⊐ Ti 和 q = σ(Ti) (當q 等於m 時,說明模式字串P整個是Ti的後綴,也意味着匹配查找成功了),所以有σ(Ti)= q,得出永動機也支持下面的等式(終態函數也是抽象的,轉化爲後綴函數表達式後,能夠用code表示):

φ(Ti) = σ(Ti)(i = 0,1,...n)        <等式二>



二、同時有兩個引理(具體證實能夠參考算法導論):

  • 引理一、後綴函數不等式:

            σ(xa) ≤ σ(x) + 1 (對於任何字符串x,以及字母a)

  • 引理二、後綴函數遞歸引理:

        對於任何字符串x,以及字母a,若是q = σ(x),有:
            σ(xa) = σ(Pqa)


<等式二> 能夠用數學概括法證實,具體以下:
一、當i = 0,由於T0 = ε,所以有φ(Ti) = 0 = σ(Ti)
二、假設φ(Ti) = σ(Ti),證實φ(Ti+1) = σ(Ti+1),用q 表示φ(Ti),用字母a表示T[i+1],有:
φ(Ti+1) = φ(Tia) (Ti+1 == Tia)
             = δ(φ(Ti),a) (根據終態函數的定義)
            = δ(q,a) (根據q的定義)
            = σ(Pqa) (根據等式一)
            = σ(Tia) (根據引理二)
            = σ(Ti+1) (Ti+1 == Tia)


從上面能夠知道當讀入T i 的終態(亦即讀入T[i]後轉移函數狀態)等於模式長度,就匹配成功了,下面是有限自動機機匹配算法僞代碼:


下面就是根據<等式一>來實現轉移函數的僞代碼:


下面是我本身實現的code:

#include <iostream>
#include <string.h>
using namespace std;

#define MIN(X,Y) 	((X)<=(Y) ? (X) : (Y))

#define MAX_NEEDLE_LEN		0xFF
#define MAX_ALPHABET_NUM	0XFF

/*
check if needle[0~k-1] is suffix of needle[0~q-1 'ch']
 */
int is_suffix(const char * needle, int k, int q, unsigned char ch)
{
	int i = 0;
	int suffix_len = k;

	if(NULL == needle || suffix_len < 0 || suffix_len > 7)
	{
		return -1;
	}

	if(needle[suffix_len - 1] != ch)
	{
		return -1;
	}

	if(1 == suffix_len)
	{
		return 0;
	}

	for(i=0; (suffix_len > 1)&&(i < suffix_len) && (i < q); i++)
	{
		if(needle[suffix_len - 2 - i] != needle[q - 1 - i])
		{
			break;
		}
	}

	if(i >= q)
	{
		return 0;
	}

	return -1;
}

unsigned int delta[MAX_NEEDLE_LEN][MAX_ALPHABET_NUM] = {0};
void compute_transition_func(const char *haystack, const char *needle)
{
	int q = 0, k = 0, j = 0;
	int n = strlen(haystack);
	int m = strlen(needle);

	for(q = 0; q < m; q++)
	{
		for(j = 0; j < n; j++)
		{
			k = MIN(m+1,q+2);

			do
			{
				k--;
				if(0 == is_suffix(needle, k, q, haystack[j]))
				{
					break;
				}
			}while(k > 0);

			delta[q][haystack[j]] = k;
		}
	}
}

void finite_automaton_matcher(const char *haystack, const char *needle)
{
	unsigned int n = strlen(haystack);
	unsigned int m = strlen(needle);
	int q = 0, i =0, j = 0;
	int reCheck_Pos_array[0xff] = {0};
	unsigned int reCheck_Index = 0;

	unsigned need_reCheck = 0;
	unsigned reOccurence = 0;

	if(needle[0] == needle[1])
	{
		need_reCheck = 1;
	}

	for(i = 0; i< n; i++)
	{
		//printf("q=[%d], haystack[%d]=[%c] delta[%d][%c]=[%d]\n", q, i, haystack[i], q, haystack[i], delta[q][haystack[i]]);
		q = delta[q][haystack[i]];

		if(q == m)
		{
			printf("\nSuccess find at haystack[%d] !\n", i-m+1);
		}

		if(needle[0] == haystack[i])
		{
			reOccurence++;
			if(reOccurence >=2)
				reCheck_Pos_array[reCheck_Index++] = i-1;
		}
		else
		{
			reOccurence = 0;
		}
	}

	if(0 == need_reCheck)
		return;

	printf("Need recheck ,and reCheck times [%d]\n", reCheck_Index);

	for(i = 0; i< reCheck_Index; i++)
	{
		q = 0;
		for(j = 0; j< m; j++)
		{
			//printf("q=[%d], haystack[%d]=[%c] delta[%d][%c]=[%d]\n", q, reCheck_Pos_array[i] + j, haystack[reCheck_Pos_array[i] + j], q, haystack[reCheck_Pos_array[i] + j], delta[q][haystack[reCheck_Pos_array[i] + j]]);
			q = delta[q][ haystack[ reCheck_Pos_array[i] + j ] ];

			if(q == m)
			{
				printf("Recheck RSuccess find at haystack[%d] !\n", reCheck_Pos_array[i]);
			}
		}
	}
}

void main()
{
	char needle[] = "aabab";
	char haystack[] = "aaababaabaababaab";

	//compute delta
	compute_transition_func((const char *)haystack, (const char *)needle);

	finite_automaton_matcher((const char *)haystack, (const char *)needle);
}

後面在測試的時候,發如今「aaababaabaababaab」裏面查找「aabab」會有遺漏,你們能夠debug看下,我針對這種狀況做了處理,部分要回過頭重複檢查, 會影響效率,沒想到好方法,歡迎你們指點~~


ps : 2014-08-06日,本身回顧下有限自動機,發現部分錯漏,本身從新看了遍算法導論,修正了一遍,以此記錄。經過回顧,本身概括下字符串匹配自動機主要是在某個字符匹配失敗時,利用已知匹配失敗的部分,看是否有部分是模式串P的前綴,以便跳過部分以節省時間。關鍵是後綴函數的理解, 固然後面幾篇模式匹配算法也有用到其餘方法,也有幾種方法結合的,具體要看業務須要。


參考:

一、《Introduction to algorithms》

相關文章
相關標籤/搜索