參見原文
簡單模式匹配算法
經典的模式匹配,是讓模式串的首字符從主串的首字符開始向右匹配。
匹配的規則是:
1.在主串的第i個位置,讓pos=i,如果主串pos位置的字符等於模式串的第一個字符,則pos++,再與模式串的第二個字符進行匹配。
2.如果又相等則繼續向下匹配,直到匹配到模式串的結尾,就說明主串中存在該模式串;如果不相等,則停止匹配,回到主串中,並i++。
3.然後再讓pos=i,重複上述步驟,直到i到達主串結尾。
相應代碼如下:
#include<stdio.h>
#include<string.h>
int main() {
char str[10000],pat[10000];
printf("please push the main string:\n");
scanf("%s",str);
printf("please push the pattern:\n");
scanf("%s",pat);
int lenS=strlen(str),lenP=strlen(pat);
//從主串第一位置開始向後匹配,直到結尾
for(int i=0;i<lenS;i++)
{
int pos=i,j=0;
while(j<lenP)
{
//如果主串pos位置與模式串的j位置相等,則都向後一位
if(pat[j]==str[pos])
{
j++;pos++;
}
//否則中斷與模式串的匹配
else break;
}
//判斷與模式串是否完全匹配,若是則輸出i位置
if(pos-i==lenP) printf("\npattern start in %d",i);
}
}
KMP模式匹配算法
KMP算法的好處就是不用回溯,而是以一種滑動的方式向下匹配模式串。
假設next數組
首先,我們假設對於模式串有一個next數組。
該數組值的含義是:模式串第0位後長度爲next[j]的字符串,與第j位(從0開始,除0外)前長度爲next[j]的字符串相等,同時next[j]值要竟可能大,但兩個字符串又不能重合。特例:第0位的next值爲-1。
例如:abcac模式串的next數組值依次爲[-1,0,0,0,1]。模式串中第4位(從0開始)字符c的next值爲1,表明字符c前長度爲1的字符串即a,與從0位開始長度爲1的字符串即a相等。其餘位,除0位外,next值都爲0。
再例如:0001模式串的next數組值依次爲[-1,0,1,2]。
利用next數組進行模式匹配
在普通模式匹配中,主串pos位與模式串j位不匹配時,主串回到i+1位重新與模式串0位進行匹配。
有了next數組,我們便不用回溯到i+1位重新匹配了。而是用主串pos位與模式串next[j]位進行匹配,若還是不匹配,則轉到next[next[j]]。直到next的值等於-1,模式串滑動到主串pos+1位重新匹配。
其實,當不匹配求next[j]值時,就是將模式串向下滑動的過程。其原理是因爲next數組的特殊性:
模式串第0位後長度爲next[j]的字符串,與第j位(從0開始,除0外)前長度爲next[j]的字符串相等。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// 獲取模式串的next數組函數:
// 將模式串既當作“主串”又當作模式串,同樣按照KMP的算法進行匹配求next值。
void getNext(char *str,int *next,int n) {
next[0]=-1;
int i=1;
int j=-1;
while (i<n) {
// 如果j==-1,則“主串”i位置的next值爲0。
// 前進到“主串”的下一位,並使模式串從頭開始匹配
if (j == -1) {
next[i]=0;
i++;
j=0;
}
// 如果“主串”i位置的前一位和模式串j位置相同,則“主串”i位置的next值爲j+1。
// 同時“主串”與模式串繼續向下匹配。
else if (str[i-1] == str[j]) {
next[i]=j+1;
i++;
j++;
}
// 如果不匹配,則前進到模式串的next[j]位置
else j=next[j];
}
}
int main() {
char str[10000],pat[10000];
printf("please push the main string:\n");
scanf("%s",str);
printf("please push the pattern:\n");
scanf("%s",pat);
int lenS=strlen(str),lenP=strlen(pat);
// next數組:當模式串第j位與主串當前第i位不匹配時,將模式串向後滑動,
// 用模式串中第next[j]位再與主串中的第i位比較。
// 若next[j]==-1,則將模式串整體向後移動一位,與主串中第i+1位繼續比較。
int *next=(int*)malloc(sizeof(int)*lenP);
getNext(pat, next, lenP);
int i=0,j=0; // i爲主串中字符位置,j爲模式串中字符位置
while (i<lenS) {
// 如果j==-1,則模式串整體向後移動一位,與主串中第i+1位比較
if (j == -1) {
i++;
j=0;
}
// 如果主串i位置與模式串j位置相等,則i、j都向下繼續配對
else if (str[i] == pat[j]) {
i++;
j++;
if (j == lenP) {
printf("find pattern in %d\n", i-lenP);
j=0;
}
}
// 如果模式串j位置與主串i位置不匹配,則將模式串向後滑動,
// 用模式串的next[j]位置與主串i位置匹配
else j=next[j];
}
free(next);
return 0;
}