字符串匹配问题是计算机编程中一个很常见的问题。最近在学习小甲鱼的数据结构与算法,正好学到字符串匹配的经典算法—KMP算法。在小甲鱼图文并茂的解说下,加上动手实践,基本上弄清了算法的关键所在。该算法的核心在于求取待匹配串的 next 数组,为了更好的解释算法过程。定义一个结构体String,它有两个成员,一个字符型指针变量 str 用来存储字符串,一个整形变量 length 用来存储字符串的长度。
1.next 数组的求解过程
以一个字符串 T="ababaaaba" 为例来进行说明:
第1步:i=1,j=0 next[1] = 0;
第2步:i=2,j=1 next[2] = 1;
第3步:i=3,j=1 ,由于 T[2]≠T[1],则j=next[j]=next[1]=0,当j=0时,j=1,next[3]=j=1;
第4步:由于 T[3]=T[1],则 i=i+1=4,j=2,next[4]=j=j+1=2
第5步:由于T[4]=T[2],则 i=i+1=5,j=j+1=3,next[5]=j=3
第6步:由于T[5]=T[3],则 i=i+1=6,j=4,next[6]=j=4
第7步:由于T[6]≠T[4],则j=next[4]=2,又由于T[6]≠T[2],则j=next[2]=1,此时 T[6] = T[1} ,则i=i+1=7,j=j+1=2,next[7]=j=2
第8步:由于T[7]≠T[2],则j=next[j]=next[2]=1,此时T[7]=T[1],则 i=i+1=8,j=j+1=2,next[8]=j=2
第9步:由于T[8] = T[2},则i=i+1=9,j=j+1=3,next[9]=j=3,到达串尾,整个过程结束。
小结: 我个人觉得记住这个过程的一个诀窍是看当前点之前的串,向左和向右有几个重复的字符,对应的 next 就等于相等的字符数加 1 .例如
2.KMP匹配的过程
待匹配的串的next数组求出之后,匹配就比较容易了。从指定点开始匹配,当遇到不匹配的情况的时候,利用next数组的值改变匹配串匹配的起始位置,直到匹配结束或者到达串尾。
3.C++代码实现
#include <iostream>
using namespace std;
typedef struct
{
int length; // 用来记录字符串的长度
char *str; // 用来存储字符串
}String;
// 求出待匹配串的next数组,用来指导后续的匹配
void get_next(String T, int *next)
{
int i = 1;
int j = 0;
next[1] = 0;
while( i < T.length)
{
if( 0 == j || T.str[i] == T.str[j])
{
i++;
j++;
next[i] = j;
}
else
{
j = next[j];
}
}
}
// 在字符串 S 中匹配字符串 T
// 匹配成功,返回匹配点的位置
// 匹配失败,返回0
int KMP(String S, String T, int pos)
{
int i = pos;
int j = 1;
int next[255];
get_next(T, next); // 获得最高指导方针,next数组
while( i <= S.length && j <= T.length )
{
if(0 == j || S.str[i] == T.str[j] ) // 必须要加上 j==0 ,否则后面会出现next 引用越界的的情况
{
i++;
j++;
}
else
{
j = next[j];
}
}
if( j > T.length ) // 如果 j 的值大于匹配串的长度,说明匹配成功。返回匹配点的位置
{
return i - T.length;
}
else
{
return 0;
}
}
int main()
{
String S,T;
char *str = " ababaaaba";
S.str = str;
S.length = strlen(str)-1; // 排除串前的空格符
char *str1 = " baa";
T.str = str1;
T.length = strlen(str1)-1;
int k;
k = KMP(S, T, 0);
if( 0 == k )
{
cout << "匹配失败!" << endl;
}
else
{
cout << "匹配成功! 匹配点为: " << k << endl;
}
/////////////////////////////////////
// 显示 S 串的 next 数组
int next[255];
get_next(S, next);
for(int i=1; i <= S.length; i++ )
{
cout << next[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
运行结果为: