從暴力匹配到KMP算法
前言
現在有兩個字符串:\(s1\)和\(s2\),現在要你輸出\(s2\)在\(s1\)當中每一次出現的位置,你會怎麽做?
暴力匹配算法
基本思路
用兩個指針分別指向當前匹配到的位置,並對當前狀態進行分類討論:若相同則繼續往下匹配,否則回溯
大致思路
用\(i\)來存儲\(s1\)當前匹配到的位置,用\(j\)來存儲\(s2\)當前匹配到的位置,則可得初始狀態下\(i=j=0\)。
對於當前狀態,有兩種可能性:
①:\(s1[i]==s2[j]\)。則\(i++,j++\)
②:\(s1[i]!=s2[j]\)。則\(i-=(j-1),j=0\)
評價
時間復雜度:\(O(nm)\)。顯然,這個方法效率並不高,每一次回溯要耗去大量時間,能不能進行優化呢?
\(KMP\)算法
簡介
\(KMP\)算法是對暴力匹配算法的改進,由\(D.E.Knuth\),\(J.H.Morris\)和\(V.R.Pratt\)同時發現,因此人們稱它為\(Knuth-Morris-Pratt\)算法(簡稱\(KMP\)算法)。
基本思路
\(KMP\)算法的關鍵是利用匹配失敗後的信息,盡量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個\(Next\)函數,函數本身包含了模式串的局部匹配信息。
大致思路
還是用\(i\)來存儲\(s1\)當前匹配到的位置,用j來存儲\(s2\)當前匹配到的位置,則可得初始狀態下\(i=j=0\)
對於當前狀態,有兩種可能性:
②:\(s1[i]!=s2[j]\)。則\(j=Next[j]\)(i不變)
其中\(Next\)數組存儲的是當前這一位的部分匹配值(這在後面會詳細介紹),所以只要讓\(j\)變成\(Next[j]\),就可以繼續對當前字符串進行匹配了,省去了i回溯所耗去的大量時間
\(Next\)數組
在匹配過程中,你可以發現一個基本事實是:當\(s1[i]\)與\(s2[j]\)不匹配時,你其實知道前面\(j-1\)字符是什麽。
\(KMP\)算法的想法是,設法利用這個已知信息,不要把"搜索位置"移回已經比較過的位置,繼續把它向後移,這樣就提高了效率。
所以,我們就可以把當前所得到的部分匹配值給求出來。又由於對於同一個字符串,部分匹配值是固定不變的,所以可以把它存在\(Next\)數組裏。
那麽\(Next\)數組怎麽求呢?
\(Excerpt\)
求\(Next\)數組的過程就是一個\(KMP\)的過程。
首先,令\(i=0\),\(j=-1\),\(Next[0]=-1\),且當前要求的是\(Next[i+1]\)。則對於當前狀態,有兩種可能性:
①\(j==-1\)或\(s2[i]==s2[j]\)。則\(i++,j++,Next[i]=j\)
②\(j!=-1\)且\(s2[i]!=s2[j]\)。則\(j=Next[j]\)//把\(j\)賦值為\(j\)的部分匹配值
這樣就可以輕松求出\(Next\)數組了。
代碼
#include<bits/stdc++.h>
#define N 1000000
#define pc(ch) (pp_<100000?pp[pp_++]=ch:(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=ch))
int pp_=0;char pp[100000];
using namespace std;
int len1,len2,Next[N+5];//len1存儲s1的長度,len2存儲s2的長度,這樣不用調用strlen(),strlen()會超時;Next[]存儲部分匹配值
char s1[N+5],s2[N+5];
inline void write(int x)
{
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void GetNext()//求出Next[]數組
{
register int i=0,j=Next[0]=-1;//初始化
while(i<=len2)//類似於一個KMP的過程
{
if(j==-1||s2[i]==s2[j]) i++,j++,Next[i]=j;
else j=Next[j];
}
}
int main()
{
register int i=0,j=0;
scanf("%s%s",s1,s2),len1=strlen(s1),len2=strlen(s2),GetNext();
while(i<=len1)//KMP的過程
{
if(j==-1||s1[i]==s2[j]) {++i;if(++j==len2) write(i-len2+1),pc('\n'),j=Next[j];/*如果找到答案就輸出*/}
else j=Next[j];//如果匹配失敗,就更新j為其部分匹配值
}
for(i=1;i<=len2;++i) write(Next[i]),pc(' ');//依照題意輸出Next[]數組
return fwrite(pp,1,pp_,stdout),0;
}
從暴力匹配到KMP算法