字串模式匹配(簡單模式匹配演算法與KMP演算法)(一)
一般的字串模式匹配演算法是類似下面的逐次匹配,舉例說明如下
主串s=ababcabcacbab
從串t=abcac
一般匹配方法如下圖所示
程式碼如下
int index(string s,string t)
{
int i=0,j=0;
int l1=s.length();
int l2=t.length();
while(i<=l1-1&&j<=l2-1)
{
if(s[i]==t[j])
{
++i;
++j;
}
else
{
if(j==0)
{
++i;
}
else
{
i=i-j+2;
j=0;
}
}
//cout<<"i="<<i<<"j="<<j<<endl;
}
if(j>l2-1) return i-l2;
else return 0;
}
這樣算下來的時間複雜度是O(l1*l2),因為每次只要中間發生字串s[i]和t[j]不相等,這種演算法會把字串t的索引置為0,而主串s也是從之前開始匹配的i加一個1,其實我們可以發現,中間有些比較是不必要的,比如從第三趟比較就可以看出主串中第4,5,8個字串是‘b’,'c','a',(對應模式串中的第2,3,4個字元)。而模式串t中第一個字元是a,所以其實不需要和這幾個字元相比較了,只需要將模式向右滑動3個字元即可。這樣的匹配過程中,實際上主串i沒有回溯,只是模式串的j在變化,就降低了時間複雜度為O(l1+l2),這也就是kmp演算法。
kmp演算法每趟比較過程讓模式串向後滑動一個合適的位置,這個合適的位置我們可以算出來,一般稱這個位置叫next表。
先寫出next表的定義,接下來再解釋為什麼這樣定義
結合這個圖來解釋
先說一下,上面兩個圖中的S0和T0分別代表的是兩個穿的長度,真正的字串都從下標1開始。
1)當j=1時,也就是說模式串的第一個字元就與當前位置s對應的字元不同,此時主串的下標應該+1,再和模式串第一個字元進行比較;
2)當兩個串匹配到此處時,前j-1個字元都是匹配的,到了第j個字元就不同了,此時需要把字串t向右移動max{k}個位置。
這個k應該滿足1<k<j並且p1...pk-1=pj-k+1...pj-1.k的值可能有多個,為了不使向右移動丟失可能的匹配,選擇最大的一個k,也就是max{k},其最大值為j-1;
3)除了上面兩種情況,發生匹配時,主串指標i不回溯,在最壞情況下,模式串從第1個字元開始與主串第i個字元比較,以便不丟失可能的匹配。
上面講解的next函式表的定義,然後下面是求next函式表的程式碼以及實現kmp演算法。篇幅有點長,轉到下一篇講。