也就是查找子串的算法 比如在akchsduiyfuwasjjvafnameyihujrlbkjee查找name,明显就是应该交给计算机干的活
BF算法
Brute Force 暴力穷举。计算机的专属工作,也可以交给闪电侠 也就是从目标字符串S的第一个字符开始与模式字符串T进行一一比较,若出现不想等的字符则从S的第二个字符开始再与T进行一一比较,直到找到匹配的子串or找不到匹配的子串
int BF(char *s,char *t)
{
int sl=0 ,tl=0 ;
for (;s[sl];++sl);
for (;t[tl];++tl);
int index = 0 ;
for ( ; index <= sl-tl ; ++index )
{
int si = index ;
int ti = 0 ;
for (;ti<tl && s[si]==t[ti];++si,++ti);
if (ti == tl) return index ;
}
return -1 ;
}
时间复杂度O(n*m),n目标串长度,m模式串长度
KMP算法
算法思想
发现问题:当从匹配起点(index)匹配到某处(si)发现不相等时,BF是将起点移动到了(index + 1)继续从头开始匹配。这有点不对劲。 KMP的想法是,我的目标串的扫描已经从index到了si,并不想回到index+1。那么Knuth、Morris和Pratt同时在研究这个问题并同时发现了KMP算法。于是叫KMP算法了。。。 请允许我介绍我自己常用的一个表示方法:字符串str,以str[a,b]表示从str[a]开始到str[b]结束的子串。 研究以index为起点匹配到s[si]!=t[ti]时,如果继续以(index,si)之间的某点为起点来从头开始匹配,其实也就是企图找到这样一个点temp,满足index < temp < si且串s[temp,si-1]==t[0,si-temp-1]。 而由于从index为起点一直匹配直到si才不相等,也就意味着s[index,si-1]==t[0,ti-1],也就有s[temp,si-1]==t[temp-index,ti-1]。再加上上文的条件,也就有t[0,si-temp-1]==t[temp-index,ti-1](条件1)。 也就是说模式串应当满足(条件1)才值得去寻找这样一个点temp。如果满足条件1,那么应当继续从si=si和ti=si-temp处继续匹配即可。此时并不需要从目标串重新规定起点,而是仍然从不匹配的si处继续开始匹配,不需要回滚,意味着目标串只需要扫描一边。匹配成功后的返回可以减去模式串的长度得到起点。 (条件1)可以化简成比较友好的形式
t[0,si-temp-1]==t[temp-index,ti-1] (1)由条件1得到 设next==si-temp-1 (2)设 si-index==(si-temp)+(temp-index)==ti (3)由原意得到 (si-temp)==next+1 (4)由(2)得到 (temp-index)==ti-(si-temp) (5)有(3)得到 (temp-index)==ti-next-1 (6)将(4)代入(5)中得到 t[0,next]==t[ti-next-1,ti-1] (7)将(2)(6)代入(1)中得到 如果满足了(条件1),可见这是个只依赖于模式串的存在,对于通常是短短的模式串来说,完全值得这个开销去得到这个“跳跃值”。之所以为跳跃,是因为当s[si]!=t[ti]的时候,只需要把ti移动到(0,ti)之间的某个值next,使得t[0,next]==t[ti-next-1,ti-1]即可。 于是设立一个next数组,用来指示ti的跳跃点
算法代码
int KMP(char *s,char *t)
{
int *next = new int [tl];
int i=0 ,j=-1 ;
next[0 ]=-1 ;
while (i<tl)
{
if (j==-1 ||t[i]==t[j])
{
++j;
++i;
next[i]=j;
}
else
{
j=next[j];
}
}
int si=0 ,ti=0 ;
while (si<sl && ti<tl)
{
if (ti==-1 || s[si]==t[ti])
{
++ti;
++si;
}
else
{
ti=next[ti];
}
}
if (ti==tl) return si-tl;
else return -1 ;
}
啊啊啊等我再画图