KMP之next数组
例题
字符串匹配
Description
给出两个字符串S和T,请判断T是否为S的子串。
Input
第一行是一个整数N,说明有多少个测试用例。
接下来是N个测试用例,每个测试用例占2行:第一行是字符串S,第二行是字符串T,字符串中不含空格。 1 ≤ strlen(S) , strlen(T) ≤ 10000
Output
对每个测试用例,输出一行结果:是否子串,是则输出"yes" ,否则输出 “no”
Sample Input
2
aabcdd
abc
aaaaaaaaaaaaa
aaaaaab
Sample Output
yes
no
直接朴素暴力查找:O(n^2)
这个代码就不给了
当使用KMP后明显就会快很多~。
KMP算法的详细介绍
对了,关于KMP算法的详细介绍在这里,灰常灰常详细,一定可以看懂的
KMP领取大门在这快去
next数组:
为什么next[0]=-1&next数组含义?
一
你必须明确next数组的含义:next[j]记录的是第j-1个字符前缀(从首字符开始到当前的上一个字符(j-1)的长度)与后缀 (不包括首字符,第二个字符到当(j-1)字符的长度)的公共元素的最大长度,或者说next[j]是位置j的最长公共真前缀真后缀的长度。
真前缀当然是不包括本身元素的,当我们只有一个元素是他并没有前缀和后缀,如果你理解为错误理解next数组(是模式串需要移动到的位置,虽然这样做是没问题的,但并不是next的真正意义 ),你在想为什么不能是next[0]=0;这样是错误的,会导致后面的next数值出错,因为长度为0的字符串并没有前缀后缀。
你可以想象成在首字符的前端-1的位置加一个哨兵(就是与所有字符都通配的字符,就是首字符都失配,就是要让模式串往后移一位),故在KMP比较时会有
if(j==-1||s[i]==p[j])
{
j++;
i++;
}
二
直接理解next[j]的数值表示的就是(j-1)的前后缀的最长公共长度,假如失配与位置j,下次开始匹配的位置,也就是已匹配的字符串(next[j]长度的字符串)下次不用再进行一一匹配了(因为他们是前后缀共有的)。
示意草图:
子串:AB……ABC…… [下标从0开始]
假如子串(匹配串)与主串(模式串)失配于C,next[c的下标号]=2。你看看截止于C的子串的前后缀共有的子串是AB,我们主串失配于C,说明主串有至少是有“
AB……AB[这里不是C]…… ”,KMP匹配是不会回退主串的,即此时主串的匹配索引(就是下面KMP匹配代码的i)不用变,而是移动子串的索引至2的位置,因为前后缀是相同的(AB)就没必要继续比了,这样就实现了主串索引不回退,这也就是KMP时间复杂度为线性的原因!
代码模板
void getnext(char *p)
{
int lenp=strlen(p);
nextt[0]=-1;
int k=-1;
int j=0;
while(j<lenp-1)
{
//p[k]表示前缀,p[j]表示后缀(并没有“真”!)
if(k==-1||p[j]==p[k])//j在这
{
j++;//j+1在这
k++;//k=k+1
//---->若p[k]=p[j],则next[j+1]=next[j]+1;
//下一个位置的next
if(p[j]!=p[k])//当p[k]!=p[j]时,与主串不匹配时可以返回到这
{
nextt[j]=k;
}
else//当p[j]=p[k]时与主串匹配时你在j位置不匹配匹配串返回这里当前
//还是 不能与主串匹配,然后还是要返回next[]即next[next[k]]
nextt[j]=nextt[k];//优化了
}
else
{
k=nextt[k];
}
}
return;
}
KMP匹配:
int KMP(char *s,char *p)
{
int i=0;
int j=0;
int lens=strlen(s);
int lenp=strlen(p);
while(i<lens&&j<lenp)
{
//如果j=-1(表示第一次开始或者重新开始匹配),即失配于首字符
if(j==-1||s[i]==p[j])
{
j++;
i++;
}
else
{
//如果j!+-1,或者当前匹配失败 ,则
j=nextt[j]; // i不变,j=next[j]
}
}
if(j==lenp)
return 1;//匹配成功
else
return 0;
}
以下是问题的输入:
int main(void)
{
int t;
char a[10005],b[10005];
scanf("%d",&t);
while(t--)
{
scanf("%s",a);
scanf("%s",b);
if(strlen(a)>=strlen(b))
{
getnext(b);
if(KMP(a,b)==1)
printf("yes\n");
else
printf("no\n");
}
else
{
getnext(a);
//getnext(b);
if(KMP(a,b)==1)
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}