《数据结构(严蔚敏版)》学习笔记(五)——串的模式匹配算法
主串S,模串T,串首存串长(SString)
普通方法:当S[i]与T[j]失配时,i回溯到i-j+2,j回溯到1,重新开始匹配。T = O(m*n);
KMP方法:当S[i]与T[j]失配时,i不变,j根据失配字符的next值滑动,继续匹配。若j滑到0,则i++,重新匹配。T = O(m+n);
kmp算法难点:求模式串的next数组!!
j 1 2 3 4 5 6 7 8
模式串 a b a a b c a c
next[j] 0 1 1 2 2 3 1 2
j 1 2 3 4 5
模式串 a a a a b
next[j] 0 1 2 3 4
nextval[j] 0 0 0 0 4
下面是模式匹配算法和动态演示next数组求法的程序...
<span style="font-size:14px;">/*-----$串的模式匹配算法$-----*/
//注:SString 的串,S[0]存串的长度
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
//初始定义
typedef int Status;
typedef char ElemType;
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define MAX_SIZE 100
typedef unsigned char SString[MAX_SIZE];
void CreateString(SString &S,char T[])
{
int i;
S[0] = strlen(T);
for(i =0;i<=S[0];i++){
S[i+1] = T[i];
}
}
/*算法[求子串位置]---T = O(n*m)*/
//返回子串T在主串S第pos个字符之后的位置
int Index(SString S,SString T,int pos)
{
int i = pos,j = 1;
while(i<=S[0] && j<=T[0]){
if(S[i] == T[j]){
++i; ++j;
}else{
i = i-j+2; j=1;
}
}
if(j>T[0]) return i-T[0];
else return 0;
}
/*算法[KMP]---T = O(n+m)*/
//求模式串T的next函数值
void get_next(SString T,int next[])
{
int i = 1,j = 0;
next[1]=0;
while(i<T[0]){
if(T[i] == T[j] || j==0){
++i; ++j; next[i] = j;
}else{
j = next[j];
}
}
}
//求模式串T的nextval函数值
void get_nextval(SString T,int nextval[])
{
int i=1,j=0;
nextval[1] = 0;
while(i<T[0]){
if(T[i] == T[j] || j==0){
++i; ++j;
if(T[i]!=T[j]) nextval[i] = j;
else nextval[i] = nextval[j];
}else{
j = nextval[j];
}
}
}
//失配时,i不变,j回溯到next[j]处重新比较
int Index_KMP(SString S,SString T,int pos)
{
int i = pos,j = 1,nextval[MAX_SIZE];
get_nextval(T,nextval);
while(i<=S[0] && j<=T[0]){
if(S[i] == T[j] || j == 0){
++i; ++j;
}else{
j = nextval[j];
}
}
if(j>T[0]) return i-T[0];
else return 0;
}
//展示KMP中求next数组流程——DisplayKMP函数 和 dynamic_next函数
void DisplayKMP(SString T,int i,int j,int next[])
{
int n = T[0],k;
for(k=0;k<=n;k++)
printf("%d ",k);
printf("\n");
for(k=0;k<2*i;k++)
printf(" ");
printf("i");
printf("\n ");
for(k=1;k<=n;k++)
printf("%c ",T[k]);
printf("\n ");
for(k=1;k<=n;k++)
printf("%c ",T[k]);
printf("\n");
for(k=0;k<2*j;k++)
printf(" ");
printf("j");
printf("\n ");
for(k=1;k<=i;k++)
printf("%d ",next[k]);
printf("\n-------------------\n");
Sleep(1000);
}
void Dynamic_next(SString T)
{
int next[100];
int i = 1,j = 0;
next[1]=0;
system("cls");
DisplayKMP(T,i,j,next);
system("cls");
while(i<T[0]){
if(T[i] == T[j] || j==0){
++i; ++j; next[i] = j;
}else{
j = next[j];
}
DisplayKMP(T,i,j,next);
system("cls");
}
}
int main()
{
SString S,T;
char input[100] = {"I love programming on my computer!"};
char model[100] = {"abaabcabbaabc"};
CreateString(S,input);
CreateString(T,model);
printf("index at %d in %s\n",Index(S,T,0),input);
printf("index_KMP at %d in %s\n",Index_KMP(S,T,0),input);
Sleep(3000);
Dynamic_next(T);
printf("\n");
return 0;
}</span><span style="font-size:24px;">
</span>
传统算法核心:
<span style="font-size:14px;">while(i<=S[0] && j<=T[0]){
if(S[i] == T[j]) {++i; ++j;}
else {i = i-j+2; j=1;}
}</span>
kmp算法核心:
<span style="font-size:14px;">while(i<=S[0] && j<=T[0]){
if(S[i] == T[j] || j==0) {++i; ++j;}
else {j = next[j];}
}</span>
算法难点——求next和nextval值:
<span style="font-size:14px;">while(i<T[0]){
if(T[i] == T[j] || j==0) {++i;++j; next[i] = j;}
else {j = next[j];}
}
while(i<T[0]){
if(T[i] == T[j] || j==0) {++i;++i;
if(T[i]!=T[j]) nextval[i] = j;
else nextval[i] = nextval[j];}
else {j = nextval[j];}
}</span>
总结:
在求模式串的next值时,
1.若T[i]与T[j]匹配:则T[j+1]的next = T[j]的next+1;
2.若T[i]与T[j]失配:则T[j]要和T[next[j]]匹配,即j = next[j];
3.若一直匹配失败到j=0,则T[j+1]的next = 1;
故是通过T[j]来推出T[j+1]的next值。