接着上文(地址),我們來聊一聊自動機算法(有限自動機字符串匹配算法)和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 }
當然,代碼是有bug,如果輸入的文本串中沒有模式串,可能會顯示不出來,我並沒有考慮到這點。有待改善,但是為了理解算法本身,我就不再修改太多的東西,因為上面的代碼已經比較清楚了。(好吧,其實是我比較懶,大半夜寫代碼,不容易啊)
我覺得要是理解上面的方法,和思想,再去看《算導》,應該就會稍微清楚一點了。但是《算導》上還是列出了一堆的數學公式。真心不是特別建議看這節,個人覺得會用就好,當然你能看下來都是好事。
練練手:地址 (這個更加復雜,因為∑有7個字符,以及6個字符串)
感言:這幾天心特別躁,師兄研三了,找工作挺不容易的。阿里校招從3000減到了400(據說),而且他是找c++方向的。工作不好找,因此,感慨算法是多么重要。好好學習算法,多做做項目,才是王道啊。
注:KMP留給下吧。歡迎交流。如果有什么錯的地方,歡迎指正。