HDU oj 1711 Number Sequence(KMP入門)
KMP
原理
KMP演算法是一種改進的字串匹配演算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同時發現,因此人們稱它為克努特——莫里斯——普拉特操作(簡稱KMP演算法)。KMP演算法的關鍵是利用匹配失敗後的資訊,儘量減少模式串與主串的匹配次數以達到快速匹配的目的。具體實現就是實現一個next()函式,函式本身包含了模式串的區域性匹配資訊。時間複雜度O(m+n)。
當匹配過程中,主串中第i個字元與模式中第j個字元比較不等時,僅需將模式向右滑動至模式中第k個字元和主串中第i個字元對齊,此時,模式中頭k-1個字元的子串‘ p[1]p[2]…p[k-1]’必定與主串中第i個字元之前長度為k-1的子串‘ s[i-k+1]s[i-k+2]…s[i-1]’相等,由此,匹配僅需從模式中第k個字元與主串中第i個字元比較起繼續進行。這就是KMP演算法,不懂的話可以看下面連結裡的動圖,博主很用心的演示了KMP的匹配方式
在寫KMP的時候,最重要的就是next陣列的變化賦值,這是KMP成不成功的關鍵所在。
Next陣列:
next[i](i從1開始算)代表著,除去第i個數,在一個字串裡面從第一個數到第(i-1)字串字首與字尾最長重複的個數。
構造next陣列使用的基本方法是遞推,要計算當前第i位字元的next值即計算從字串開始至第i位前(不包括第i位)的字串的最長字首與字尾重複數量。
規定第0位(第一個字元)的next值是0或-1(有不同構造next函式的方法)。
第i+1位的next值分兩種情況討論:
①當p[i]=p[j]時,字首和字尾相等,最大長度可以延續因此next[++i]=next[++j]
②當p[i
我們用例項看一下next陣列存的值
模式串為a時,無字首字尾所以next陣列為0.
模式串為ab時字首為a字尾為b,不相等,就是0
模式串aba時字首a和字尾a相等為1,還有一個字首為ab,一個字尾為ba,因為兩者不相等,所以next陣列不發生變化。
模式串為abacab時,就是2了,字首為ab字尾也為ab,但當前綴為aba時後綴為cab,不相等,所以不發生變化。
程式碼實現:
void Get_Next() { int j, k; j = 0; k = -1; nextt[0] = -1; ///我們這next陣列0代表沒有能匹配的字首和字尾 ///如果next陣列等於1代表模式串字首字尾有一個相等 while(j < tlen) ///t為模式串的長度 { if(k == -1 || T[j] == T[k]) { nextt[++j] = ++k; ///給next陣列賦值 } else { k = nextt[k];///回溯 } } }
KMP兩大操作
1)查詢模式串T在主串S中首次出現的位置
2)查詢模式串T在主串S中出現的次數
這兩大操作都是建立在next陣列的基礎上的1)
int KMP_Index()
{
int i = 0, j = 0;
Get_Next();
while(i < slen && j < tlen)///KMP是移動模式串和主串比較
{
if(j == -1 || S[i] == T[j])
{
i++;
j++; ///在主串中和模式串比較
}
else
{
j = nextt[j]; ///j的回溯
}
}
if(j == tlen) ///如果在S中找到完全匹配的串
{
return i - tlen; ///返回開始匹配的位置(從0開始)根據題目要求返回,從0開始或從1開始
}
else
{
return -1;///否則沒找到,返回-1
}
}
2)
int KMP_Count()
{
int ans = 0;
int i, j = 0;
if(slen == 1 && tlen == 1)///考慮特殊情況,模式串和主串長度都為1
{
if(S[0] == T[0])
return 1;
else
return 0;
}
Get_Next();
for(i = 0; i < slen; i++)///完全遍歷主串
{
while(j > 0 && S[i] != T[j])///如果不匹配就回溯,即相當於移動模式串直到找到下一個匹配點為止
j = nextt[j];
if(S[i] == T[j])
j++;
if(j == tlen) ///找到一個匹配
{
ans++;
j = nextt[j];///回溯,找下一個匹配
}
}
return ans;
}
這樣,我們的KMP就大體上明白了。下面我們來做一道題運用一下
實戰練習
HDU oj 1711 Number Sequence點選開啟連結
題目大意:
要求找模式串在主串中出現的第一個位置,因為是陣列,所以我們就只要稍微的改一點點就行了
思路:
很簡單的一道模板題,套模板就好了
話不多說直接上程式碼
AC程式碼:
#include<stdio.h>
#define maxn 1000010
#define maxa 10005
int a[maxn],b[maxa];
int nextt[maxa];
int n,m;
void Get_Next()
{
int j,k;
j=0;
k=-1;
nextt[0]=-1;
while(j<m)
{
if(k==-1||b[j]==b[k])
nextt[++j]=++k;
else k=nextt[k];
}
}
int KMP_Index()
{
int i=0,j=0;
Get_Next();
while(i<n && j<m)
{
if(j==-1||a[i]==b[j])
{
i++;
j++;
}
else j=nextt[j];
}
if(j==m) return i-m+1;
else return -1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<m;i++)
scanf("%d",&b[i]);
printf("%d\n",KMP_Index());
}
return 0;
}