//朴素匹配
int StrIndex (const char*str,const char* sub)
{
int i=0;
int j=0;
while (str[i]!='\0'&&sub[j]!='\0')
{
if (str[i]==sub[j])
{
i++;
j++;
}
//如果不等sub的指针退到最开始处j=0,str退到i=i-j+1,即每趟匹配str右移动一个位置,这样的算法时间复杂度是O(n*m)
//n是str的长度,m是sub的长度。
else
{
i=i-j+1;
j=0;
}
}
if (sub[j]=='\0')
{
return(i-j+1);
}
else
{
return 0;
}
}
kmp算法:
next数组的理解:http://www.cnblogs.com/10jschen/archive/2012/08/21/2648451.html
a、当前面字符的前一个字符的对称程度为0的时候,只要将当前字符与子串第一个字符进行比较。这个很好理解啊,前面都是0,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。比如agcta这个里面t的是0,那么后面的a的对称程度只需要看它是不是等于第一个字符a了。
b、按照这个推理,我们就可以总结一个规律,不仅前面是0呀,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是2了。有两个字符对称了。比如上面agctag,倒数第二个a的next是1,说明它和第一个a对称了,接着我们就把最后一个g与第二个g比较,又相等,自然对称成都就累加了,就是2了。
c. 位置i=0到14如下,我加的括号只是用来说明问题:
(a g c t a g c )( a g c t a g c) t
我们可以看到这段,最后这个t之前的对称程度分别是:1,2,3,4,5,6,7,倒数第二个c往前看有7个字符对称,所以对称为7。但是到最后这个t就没有继承前面的对称程度next值,所以这个t的对称性就要重新来求。
int k=prefix[i-1];
//不断递归判断是否存在子对称,Pattern[i] != Pattern[k]说明虽然对称,但是对称后面的值和当前的字符值不相等,所以继续递推,k=0说明不再有子对称.
while( Pattern[i] != Pattern[k] && k!=0 )
k=prefix[k-1]; //继续递归
void SetPrefix(const char *Pattern, int prefix[])
{
int len=strlen(Pattern);//模式字符串长度。
prefix[0]=0;
for(int i=1; i<len; i++)
{
//a g c t a g c a g c
//0 0 0 0 1 2 3 1 2 3
int k=prefix[i-1];
//不断递归判断是否存在子对称,k=0说明不再有子对称,Pattern[i] != Pattern[k]说明虽然对称,但是对称后面的值和当前的字符值不相等,所以继续递推
while( Pattern[i] != Pattern[k] && k!=0 )
k=prefix[k-1]; //继续递归
if( Pattern[i] == Pattern[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++
prefix[i]=k+1;
else
prefix[i]=0; //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0
}
}
在求得模式串的next后,匹配可以按如下进行:假设s是正文目标串,T是模式串,并设i和j分别指示目标和模式正待比较的字符,另i的值为pos,j 的初始值为0.在匹配过程中,若Si==Tj,则i和j分别增1,否则i不变,j退回到prefix[j-1]即模式串右边滑动,在比较Si和Tj,.....当j=-1时,此时另i和j都加1,继续比较。
int match_kmp(const char *s ,const char *t,int pos,int *prefix) //s是目标正文,t是模式串,pos主串开始比较的位置
{
int n=strlen(s);
int m=strlen(t);
SetPrefix(t,prefix);
int j=0;
int i=pos;
while (i<n&&j<m)
{
if (s[i]==t[j])//当相等是俩指示右边移动
{
i++;
j++;
}
else//不等时,i不变,j退到prefix[j-1]
{
if(j-1>-1)
j=prefix[j-1];
else //当前待比较的正文中的字符,不等于模式串中的任意字符,模式串的j退到了-1,两者同时加1右移,继续比较
{i++;j++}
}
}
if (j==m)//匹配成功
{
return i-j+1;//返回主串中第一个匹配的位置,即第几个字符开始匹配
}
else
{
return 0;//失败
}
}
时间复杂度的分析:
1.求next时的时间复杂度为模式串的长度,即for循环,故为m。
2.KMP比较时的复杂度是正文的长度,即while循环,它由正文长度决定。
故总的时间复杂度是o(n+m);远远比朴素模式比较的时间复杂度o(n*m)小。