4種字符串匹配算法:有限自動機(中)


  接着上文(地址),我們來聊一聊自動機算法(有限自動機字符串匹配算法)和KMP算法。

====#=有限自動機算法=#=====

  關於有限自動機,網上的分析的資源,大部分都很籠統,算導上的知識點,全是數學公式,看的也會特別累。因此,打算從算導的第一題開始講起。從習題入手,講這個算法的思想。

例子:對模式 P = aabab構造出相應的字符串匹配自動機,並說明它在文本字符串T=aaababaabaababaab上的操作過程。

再講這個例子之前,我們有必要先來了解一下自動機是什么意思?

有限自動機是什么意思?他是一個處理信息的簡單機器,通過對文本字符串T進行掃描,找出模式P的所有出現的位置。它們只對每個文本字符檢查一次,並且檢查每個文本字符時所用的時間為常數。

我們來看看它的偽代碼:

n=T.length
q = 0
for i = 1 to n
    q = ξ(q,T[i])
    if(q == m)
        print "..."

可以看出,他的時間復雜度為o(n),但是,這個匹配時間,並沒有包括計算轉移函數ξ的預處理時間。接下來,我們來做一做上面那個題目。

有限自動機分為5個組員(Q,q=0,A,∑,ξ)

 我們假設:Q = {0,1,2,3,4,99} //99用來表示不會取得,如果等於99就跳出循環。

q:初始化為0

A={0} //代表了終點,這里只有一個終點

∑={a,b,c,d....x,y,z}

關於函數,我設計成這樣:

aabab: {0* -> 1 -> 2 -> 3 -> 4 -> 0*}

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string>
 4 #include <stdlib.h>
 5 int main()
 6 {
 7     int a[] = { 1,2,0,0,0,
 8         0,0,0,0,0,
 9         0,0,0,0,0,
10         0,0,0,0,0,
11         0,0,0,0,0,0 };
12     int b[][5] = {
13         /*, a, b*/
14         { 99,1,99 },// 0
15         { 99,2,99 },// 1
16         { 99,99,3 },// 2
17         { 99,4,99 },// 3
18         { 99,99,0 },// 4
19 
20     };
21     char c[100];
22 loop:
23     while (std::cin >> c)
24     {
25         int str = strlen(c);
26         for (int p = 0, i = 0;i < str;i++, p = 0)
27         {
28             int count = i;
29             while (p < 99 && c[count] != '\r' && count < str) {            
30                 p = b[p][a[c[count] - 'a']];                
31                 count++;
32                 if (p == 0) {
33                     printf("YES\n");
34                     goto loop;                
35                 }
36             }        
37         }
38     }
39     std::cout << "NO" << std::endl;40 }

我們先創建一個數組a,因為字符串aabab只有a和b,所以初始化依次從1遞增,其他為0。二維數據b,保存p值。

29             while (p < 99 && c[count] != '\r' && count < str) {            
30                 p = b[p][a[c[count] - 'a']];                
31                 count++;
32                 if (p == 0) {
33                     printf("YES\n");
34                     goto loop;                
35                 }
36             }   

這段代碼,用於獲取p值。也就是ξ函數。也許,我們需要把他單獨摘出來,要不然這個函數的執行時間為O(n*m)(展開下列函數,可看到和偽代碼相同的c++代碼),但事實上,他已經比BF算法好太多了。當然他也有弊病,如果預處理的時間太長,該怎么辦?這是一個值得考慮的問題。換句話說,如果∑特別多,我們這里只有2個,建立的自動機時間也很長。我們有方法處理。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string>
 4 #include <stdlib.h>
 5 
 6 char c[100];
 7 int ppp(int &p, int &i, int &str);
 8 
 9 static int a[] = { 1,2,0,0,0,
10 0,0,0,0,0,
11 0,0,0,0,0,
12 0,0,0,0,0,
13 0,0,0,0,0,0 };
14 static int b[][5] = {
15      /*, a, b*/
16     { 99,1,99 },// 0
17     { 99,2,99 },// 1
18     { 99,99,3 },// 2
19     { 99,4,99 },// 3
20     { 99,99,0 },// 4
21 };
22 int main()
23 {
24 
25 
26 loop:
27     while (std::cin >> c)
28     {
29         int str = strlen(c);
30         for (int p = 0, i = 0;i < str;i++, p = 0)
31         {
32             int count = i;
33             p = ppp(p,i,str);
34             if (p == 0) {
35                 printf("YES\n");
36                 goto loop;
37             }        
38         }
39     }
40     std::cout << "NO" << std::endl;
41     system("pause");
42 }
43 
44 int ppp(int &p, int &i,int &str)
45 {
46     int count = i;
47     while (p < 99 && c[count] != '\r' && count < str) {
48         p = b[p][a[c[count] - 'a']];
49         count++;
50     }
51     return p;
52 }
和偽代碼格式相同的代碼 O(n) 點擊展開

當然,代碼是有bug,如果輸入的文本串中沒有模式串,可能會顯示不出來,我並沒有考慮到這點。有待改善,但是為了理解算法本身,我就不再修改太多的東西,因為上面的代碼已經比較清楚了。(好吧,其實是我比較懶,大半夜寫代碼,不容易啊)

我覺得要是理解上面的方法,和思想,再去看《算導》,應該就會稍微清楚一點了。但是《算導》上還是列出了一堆的數學公式。真心不是特別建議看這節,個人覺得會用就好,當然你能看下來都是好事。

練練手地址 (這個更加復雜,因為∑有7個字符,以及6個字符串)

 

感言:這幾天心特別躁,師兄研三了,找工作挺不容易的。阿里校招從3000減到了400(據說),而且他是找c++方向的。工作不好找,因此,感慨算法是多么重要。好好學習算法,多做做項目,才是王道啊。

 

注:KMP留給下吧。歡迎交流。如果有什么錯的地方,歡迎指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM