串-模式匹配-MP演算法
阿新 • • 發佈:2018-12-16
之前學習了KMP演算法,現在學習一下它的弱化版:MP演算法。
為啥還要學習它呢?因為它是接下來要學習的AC-自動機的基礎。
輸入:主串S,子串T
輸出:主串中子串第一次出現的位置(0-length(S-1))。匹配不到不輸出.
樣例:
S:ababcabcacbab
T:abcac
執行結果:
演算法思想:
假定在匹配的過程中正在比較文字串*位置的字元和模板串abbaaba的最後一個字元。發現二者不同(稱為失配),這時,樸素演算法會把模板串後移一位,重新比較abbaaba的第一個字元和文字串!!位置的字元。
MP演算法認為,既然!!位置已經比較過一次,就不應該再比一次了,事實上,我們已經知道灰色部分就是abbaab,應該可以直接利用模板串本身的特性判斷出右移一位一定不是匹配。同理,右移兩位或者三位也不行,但是右移四位是有可能的。這個時候,需要比較*處的字元剛和abbaaba的第三個字元。
下面其中編號為i的結點表示已經匹配了i個字元,匹配開始時當前狀態是0,成功匹配時狀態加1(表示多匹配一個字元)
而失配時沿著失配邊走。用失配函式f[i]表示狀態i失配時應轉移到新狀態,要特別注意的是f[0]=0.
void find(char *T, char *P, int *f) { int n, m, i, j; n = strlen(T); m = strlen(P); getFail(P, f); j = 0; //當前結點編號,初始為0號結點 for(i = 0; i < n; i++) //文字串當前指標 { while(j && P[j] != T[i]) //順著失配邊走,直到可以匹配 j = f[j]; if(P[j] == T[i]) j++; if(j == m) printf("%d\n", i-m+1); //找到了 } }
演算法分析:
每次j++的時候會伴隨一次i++,而每次j=f[j]的時候j至少會減1.最壞情況下j增加了n次,因此j=f[j]的次數不會超過n。
因此總時間複雜度O(n)。
狀態轉移圖的構造是MP演算法的關鍵。也是它最巧妙的地方。
演算法的思想是:用自己匹配自己。根據f[0],f[1],f[i-1]遞推f[i]。
//狀態轉移圖,用自己匹配自己,用f[0],f[1]..f[i-1] 遞推f[i]. void getFail(char *P, int *f) { int m, i, j; f[0] = 0; f[1] = 0; //遞推邊界初值 m = strlen(P); for(i = 1; i < m; i++) { j = f[i]; while(j && P[i] != P[j]) j = f[j]; f[i+1] = (P[i] == P[j]) ? (j+1):0; } }