这个算法很有技巧性,这主要是next[],它的值与匹配字符串有关系,而与主字符串没关系,它是表示副字符串(匹配的字符串)的移动位置。
当主字符串的第i个与副的第j个不匹配,就要用到next[j],这时它就决定j的值了,意思就是:j此时的位置取决于匹配的字符串的结构。此时j的移动是为了与主字符串第i个相匹配,
我们不难发现,主字符串中的 i 并没有回溯,只是副字符串在回溯,但j并不是时时刻刻一个一个地移动,因为next[j]中的值并不是固定不变的。
下面要重点弄懂next数组的来历:
比如:
主:ffe,副:deoffef。
首先,要把副字符串传递给getnext函数。
void getnext(char *e) //特别指明:next的值是字符串,自己与自己比较而得。
{
int i=0,j=-1;
next[0]=-1; //deoffef
int r=strlen(e); //deoffef 第一个与第二个比较。
while(i<r)
{ //当i等于e的字符长度就退出,表示next[]已经赋值完了
if(j==-1||e[i]==e[j]) //从第一个开始判断(字符串的第一个与第二个匹配),如果相同就一直赋值给next[i],
next[++i]=++j; // 如果匹配,就让j回溯,此时j一定会小于i,即向(j-1)的去寻找与第i个相匹配的。
else j=next[j]; //找的到,进入if语句,那么next[i]的值就是副字符中与第主字符串相同的位置。
} //如果找不到,j最终会等于-1,也会进入if语句,此时的next[i]=-1,
}
分析了getnext函数,deoffef 对应的next中的值为:-1,-1,-1,-1,-1,-1,-1。因为第一个字母d,后面的没有一个为d。
再比如 “aaaaccc”,next中的值:-1,0,1,2,-1,-1,-1。
最后,你会发现,next的值,只是为了得到第一字母与后面的字母(与第一相同的字母)的位置,以便在与主字符串相比较时,发生不相同是,就要去找主字符串中的第i个,与第j个是否相,这时,就取出next[j]的值,如果为-1,表明该字符与副字符串中第一个不同,然后,就跳到第一个字母,与主字符串第i个相比较。我们不如好好想想,假如,副字符串与主字符串有j个不相匹配,前面虽然有j-1个相匹配,但还是不成立,所以我就让它与第一个去比较,另外,不要担心中间有可能刚好相匹配,这个问题不大,因为相匹配的
字符串,next中的值只与副字符串有关,所以,如果后面不匹配,它会一次在前面找。
如何求next函数。
next值仅取决于模式串而与主串无关。可以从next函数的定义出发,用递推的方法求得next函数值。
有定义 next[0]=-1.
设 next[j]=k,即有
“t1t2......tk-1"="t(j-k+1) t(j-k+2)....tj"
这就是说next[j+1]=k+1,即
next[j+1]=next[j]+1
第二种情况:若tk不等于tj则
”t1t2.....tk"≠“tj-k+1tj-k+2.....tj"
此时可以把求next函数值的问题看成一个模式匹配问题,整个模式串即是主串又是模式,而当前在匹配的过程中,已有“t1t2......tk-1"="t(j-k+1) t(j-k+2)....tj"式成立,则当tk≠tj时应将模式向右移动,使得第next[k]个字符和”主串“中的第j个字符相比较。若next[k]=k',且tk'=tj',则说明在主串第j+1个字符前存在一个最大度为k‘的子串,使得
“t1t2......tk-1"="t(j-k+1) t(j-k+2)....tj'
因此 next[j+1]=next[k]+1
同理若tk≠tj则模式继续向右移动,使得next[k']个字符和tj对值,依次类推,直到tj和模式中的某个字符匹配成功或不存在任何k’(1<k'<k<...<j)满足,此时next[j+1]=0.
求next函数算法如下:
void getnext(char t[],int next[])
{
int i=0,j=-1;
next[0]=-1;
while(i<t[0])
{
if(j==-1||t[i]==t[j])
next[++i]=++j;
else
j=next[j];
}
}
还有所不明白:点击打开链接看这个
下面以一个列子,来看看,
题意:输入两个字符串s,t。要你把它们合并,要求是:把它相同的一段只保留一个。比如.aabat bat 答案应该是 aabat,另外,如果两者分别当作主串都没共相同的,就按照
字典循序合并,比如sfetrg aerf 输出应该是:aerfsfetrg,如果互换匹配都有公共子段,应该输出公共子段长的那种方案,比如:staa aastaa输出应该是:aastaa.
该题就是用kmp算法实现的。代码如下:
#include<stdio.h>
#include<string.h>
#define MAX 100005
int next[MAX];
void get_next(char *e)
{
int i=0,j=-1;
int r=strlen(e);
next[0]=-1;
while(i<r)
{
if(j==-1||e[i]==e[j])
next[++i]=++j;
else
j=next[j];
}
for(int i=0;i<r;i++)
//printf("%d ",next[i]);
//printf("\n");
}
int kmp(char *s,char *t)
{
int i=0,j=0;
get_next(t);
int n=strlen(s),m=strlen(t);
while(i<n&&j<m)
{
if(j==-1||s[i]==t[j])
++i,++j;
else
j=next[j];
}
// printf("*%d*",j);
return j;
}
int main()
{
char s[MAX],t[MAX];
while(scanf("%s%s",s,t)!=EOF)
{
int x=kmp(s,t);
int y=kmp(t,s);
if(x==y)
{
if(strcmp(s,t)>0)
{
printf("%s",t);
printf("%s\n",s+x);
}
else
{
printf("%s",s);
printf("%s\n",t+x);
}
}
else if(x>y)
{
printf("%s",s);
printf("%s\n",t+x);
}
else
{
printf("%s",t);
printf("%s\n",s+y);
}
}
return 0;
}
/*
cac abaabacac
*/