字符串的朴素匹配与KMP匹配

//朴素匹配
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)小。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值