kmp算法是解決單模匹配問題的算法,難點在於求next[]數組
求next[]數組:對於子串的所有前綴子串的最長公共前后綴的長度,就是next[]數組的值
首先,要了解兩個概念:"前綴"和"后綴"。 "前綴"指除了最后一個字符以外,一個字符串的全部頭部組合;"后綴"指除了第一個字符以外,一個字符串的全部尾部組合。如下圖所示:
下面再以”ABCDABD”為例,進行介紹:
”A”的前綴和后綴都為空集,共有元素的長度為0;
”AB”的前綴為[A],后綴為[B],共有元素的長度為0;
”ABC”的前綴為[A, AB],后綴為[BC, C],共有元素的長度0;
”ABCD”的前綴為[A, AB, ABC],后綴為[BCD, CD, D],共有元素的長度為0;
”ABCDA”的前綴為[A, AB, ABC, ABCD],后綴為[BCDA, CDA, DA, A],共有元素為”A”,長度為1;
”ABCDAB”的前綴為[A, AB, ABC, ABCD, ABCDA],后綴為[BCDAB, CDAB, DAB, AB, B],共有元素為”AB”,長度為2;
”ABCDABD”的前綴為[A, AB, ABC, ABCD, ABCDA, ABCDAB],后綴為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。
eg:主串為cbbbaababac 子串為ababac
初始化next[0]=-1;
子串的最長公共前后綴長度
a -->0 next[1]=0 a的前綴為空,后綴也為空,共有元素的長度為0
ab -->0 next[2]=0 ab前綴為[a],后綴為[b],共有元素的長度為0
aba -->1 next[3]=1 前綴為[a,ab],后綴為[a,ba],共有元素的長度為1
abab -->2 next[4]=2 前綴為[a,ab,aba],后綴為[b,ab,bab],共有元素的長度為2
ababa -->3 next[5]=3 前綴為[a,ab,aba,abab],后綴也為[a,ba,aba,baba],共有元素的長度為3
next[i]數組的作用是在當子串字母s[i]在和主串字母p[j]失配的時候,next[i]數組提供一個值,子串整體移動( i-next[i] )個位置,繼續用s[next[i]]去和主字母p[j]匹配
eg:模板串是cbbbaababac,子串是ababa
子串下標: 0 1 2 3 4
a b a b a
失配跳轉位置next[]: -1 0 0 1 2
這里解釋一下:當子串和主串失配的時候,就根據next[]的值移動子串到相應位置去和主串匹配。當子串next[]值為-1的時候,主串的當前匹配位置后移一個字母
這里模擬一下匹配過程,i表示主串的當前匹配位置,j表示子串的當前匹配位置,初始i=0,j=0;主串p[],子串s[]
a!=c ---> i++ i=1,j=0
a!=b ---> i++ i=2,j=0
a!=b ---> i++ i=3,j=0
a!=b ---> i++ i=4,j=0
a==a ---> i++,j++ i=5,j=1
b!=a ---> i保持不變,j=next[j],跳轉 i=5,j=0
a==a ---> i++,j++ i=6,j=1
b==b ---> i++,j++ i=7,j=2
a==a ---> i++,j++ i=8,j=3
b==b ---> i++,j++ i=9,j=4
a==a ---> i++,j++ i=10,j=5
j>=strlen(s) 匹配結束 , 返回可以匹配的首地址 return j-i+1
#include<iostream> #include<string.h> using namespace std; char p[100],s[100]; int next1[100]; void get_next(char *s,int *next1) { int m=strlen(s);//子串的長度 int j=0;//當前匹配的位置 int k=-1;//失配的時候要跳轉的位置(也是最長公共前后綴的長度) next1[0]=-1; while(j<m) { if(k==-1||s[j]==s[k]) next1[++j]=++k; else k=next1[k]; } } int kmp(char *p,char *s)//p是模板串,s是子串 { int i=0,j=0; int n=strlen(p); int m=strlen(s); while(i<n&&j<m) { if(j==-1||p[i]==s[j]) { i++; j++; } else j=next1[j]; } if(j>=m)//s串比較完畢 return i-m+1; else return -1; } int main() { cin>>p>>s; get_next(p,next1); for(int i=0;s[i];i++) cout<<"next["<<i<<"]="<<next1[i]<<endl; cout<<"從第"<<kmp(p,s)<<"個字符開始匹配"<<endl;//返回的是開始匹配的第幾個字符,不是位置 return 0; }