学习KMP算法时,了解到KMP算法主要分为两个步骤:字符串的自我匹配,目标串和模式串之间的匹配
一、字符串的自我匹配
所谓字符串的自我匹配,就是看字符串中左右侧相等的最长子串的字符个数。以字符串“12121”为例,左侧的子串为“1”、“12”、“121”或“1212”,注意“12121”不是它本身的子串; 右侧的子串为“1”、“21”、“121”或“2121”。
下面用几个具体的例子来说明:
例1:字符串1212121
从最左边开始算起,1没有子串,匹配的字符个数为0;
12的左右两侧没有相等的子串,匹配的字符个数为0;
121的左右侧有相同的子串1,匹配的字符个数为1;
1212的左右侧有相同的子串 12,匹配个数为2;
12121的左右侧有相同的子串1或121,取最长的子串121,所以匹配的字符个数为3;
121212的左右侧有相同的子串12或1212,取最长的子串1212,所以匹配的字符个数为4;
1212121的左右侧有相同的子串1、121或12121,取最长的子串12121,所以匹配的字符个数个数为5
综上所述,字符串1212121的自我匹配结果为0,0,1,2,3,4,5
不妨在据两个个例子
例2:字符串aaaa
从最左边开始算起,a没有子串,匹配的字符个数为0;
aa左右侧相等的最长的子串为a,匹配的字符个数为1;
aaa左右侧相等的子串为a或aa,取最长的子串aa,所以匹配的个数为2;
aaaa左右侧相等的子串为a或aa或aaa,取最长的子串aaa,所以匹配的字符个数为3。
综上所述,字符串aaaa的自我匹配结果为0,1,2,3
例3:字符串abaabab
从最左边开始算起,a没有子串,匹配的字符个数为0;
ab左右侧没有相同的子串,匹配的字符个数为0;
aba左右侧有相同的子串a,匹配的字符个数为1;
abaa左右侧有相同的子串a,匹配的字符个数为1;
abaab左右侧有相同的子串ab,匹配的字符个数为2;
abaaba左右侧有相同的子串a或aba,取最长的子串aba,所以匹配的字符个数为3;
abaabab左右侧有相同的子串ab,匹配的字符个数为2。
综上所述,字符串abaabac的自我匹配结果为0,0,1,1,2,3,2
从上面的3个例子中,咱们看看能否找到什么规律,这个规律可以使得咱们在字符串的自我匹配过程中不需要每次都从第一个字符开始比较呢?
以例3中的字符串str[]=”abaabab”为例,咱们将其自我匹配结果放进数组sub中,则sub[]={0,0,1,1,2,3,2}。
咱们观察到aba的sub[2]=1,说明str[0]=str[2],那么在计算sub[3]时,咱们可以先比较一下str[1]和str[3],如果str[1]等于str[3]的话,那么由原来的str[0]=str[2]咱们可以得知sub[3]=su[2]+1=2。若str[1]不等于str[3],则str[3]再与str[0]比较。
同理,由sub5]=3,可得str[0]==str[3],str[1]==str[4],str[2]==str[5],那么在计算sub[6]时,咱们先比较str[6]和str[3],若二者相等,则str中的前4个(第0,1,2,3)字符与后4个(第3,4,5,6)字符相同,这样可以得到sub[6]=sub[5]+1=4;若不相等,str[6]应该与谁相比较呢?是str[0]?还是str[1]?或是str[2]?
注意,sub[5]=3可推出str[2]==str[5]; sub[2]=1可推出str[0]==str[2];二者结合可得str[0] == str[5]。由这个结果,咱们得知str[6]要与str[1]比较,若二者相等则有sub[6]=sub[2]+1=2。若二者不相等则继续回溯让str[6]与str[0]比较。
这样,我们可以发现这样的规律:str[6]先与str[sub[5](即str[3])比较,二者不相等后str[6]是与str[sub[sub[5]-1]](即str[1])比较。这样就有了如下的递归的规律:
当i=0时,sub[0]=0
当i=1时,sub【1]=0或1(结果由str[0]是否等于str[1]来定)
当i>=2时,str[i]与str[sub[i-1]比较,若相等,则sub[i]=sub[i-1]+1。若不相等,则str[i]与str[sub[sub[i-1]-1]比较。若二者相等,则sub[i]= sub[sub[i-1]-1]+1,若不相等,令j=sub[sub[i-1]-1],则str[i]继续与str[sub[j-1]-1]比较……如此递归,直到最后str[i]与str[0]比较。
下面给出C语言实现代码:
#include <stdio.h>
#include <string.h>
int* selfMatch(char *str)
{
int sub[100];
sub【0] = 0;
int j = 0;
long n = strlen(str);
for(int i = 1; i < n; i++)
{
while (j > 0 && str[j] != str[i])
{
j = sub[j-1];
}
if(str[j] == str[i])
{
j++;
}
sub[i] = j;
}
int *pCnt = sub;
printf("字符串自我匹配的结果为:");
for(int i = 0; i < n; i++)
{
printf("%d",pCnt[i]);
}
return pCnt;
}
int main(int argc, const char * argv[])
{
char *str = "abaabab";
selfMatch(str);
return 0;
}
运行结果:
字符串自我匹配的结果为:0011232
搞一下插入代码,看看什么效果
#include<stdio.h>
#include<string.h>
int *selfMatch(char *str)
{
int sub[100];
sub[0]=0;
int i,j=0;
int n=strlen(str);
for(i=1;i<n;i++)
{
while(j>0&&str[i]!=str[j])
j=sub[j-1];
if(str[i]==str[j])
j++;
sub[i]=j;
}
int *pCon=sub;
printf("结果\n");
for(i=0;i<n;i++)
printf("%d",sub[i]);
printf("\n");
return pCon;
}
int main()
{
char *str = "abaabab";
//输入长度
int n=strlen(str);
printf("长度:%d\n",strlen(str));
int *tmp=selfMatch(str);
printf("-------------\n");
for(int i=0;i<n;i++)
{
printf("%d",tmp[i]);
}
printf("\n");
return 0;
}