字符串匹配

描述:给定一个正在编辑的文本S以及一个模式字符串P,判断P是否在S中出现,并返回P在S中的位置。

思路一:朴素算法,分别在S和P中设置指针指向第一个字符,若当前字符相同,则迭代比较后面字符;若不匹配,则令S的工作指针前移一位,指向P的指针从头迭代字符是否匹配。如图:

令当前cur指向A,依次迭代比较S,P后续元素。

找到B,C不匹配后,令当前cur后移,P从头迭代比较如图:

B,A不匹配,令cur后移,P从头迭代比较如图:


判断是否比较结束模式P最后一个元素,若是,则返回当前cur为模式P在S出现的位置。

class Solution{
public:
	const char * isMatched(const char *str,const char *pa)
	{
		const char *cur=str;
		const char *re=str;
		const char *p=pa;
		while(*(++p))//p向前移动pa长度M次 
		{
			re++;//re向前移动M-1次,令re指向当前与pa匹配的字符段最后一个元素
		}
		if(*re=='\0')return NULL;
		while(*re)
		{
			p=pa;
			while(*cur==*p)
			{
				cur++;
				p++;
			}
			if(*p=='\0')return cur;
			else
			{
				cur++;
				re++;
			}
		}
		return NULL;
    }
};
思路二:KMP算法

KMP算法是对朴素算法的改进,将时间从平方级降低到线性。KMP算法需要用到辅助数组next[M],长度与模式P一致。数组next求解如下:

当模式P为 A  B  A  C

next[0]为模式P只有前一个元素的子串{A}的前缀与后缀的最长公共长度,为0;

next[1]为模式P有前两个元素的子串{A,B}的前缀{A}与后缀{B}的最长公共长度,为0;

next[2]为模式P有前三个元素的子串{A,B,A}的前缀{A,AB}与后缀{A,BA}的最长公共长度,为1;

next[3]为模式P有前四个元素子串{A,B,A,C}的前缀{A,AB,ABA}与后缀{C,AB,BAC}的最长公共长度,为0;

因此next数组为0  0  1  0

为什么要计算每一个子串前后缀的最长公共长度呢?这是因为朴素算法中,当出现不匹配的情况时,我们只将cur前移一位。但是实际上当前位不匹配时,之前的元素都能够和模式P对应,因此我们向前移动的位数不再为1,而是移动位数 = 已匹配的字符数 - next[i],i为最后一个匹配的字符在模式P中的位置。

A  B  A  B  A  C

A  B  A  C

此时,B,C不对应,但是前面元素均一一匹配,A为最后一个匹配字符next[2]=1,移动位数 = 已匹配的字符数 - next[i]=3-1=2。因此将向前移动两位

A  B  A  B  A  C

         A  B  A  C

next求解思路:

1.当子串只有一个元素时,next[i]=0;

2.当next[i-1]==0时,我们只需要比较最后一个元素与第一个元素是否相等,如next[1]=0,我们比较最后一个元素A与第一个元素A,相等则next[2]=1.

3.依次类推,当next[i-1]==j时,我们比较最后一个元素与第(j+1)个元素是否相等,如next[2]=1,此时我们比较最后一个元素C与第二个元素B,不相等,则比较最后一个元素与第一个元素A,不相等,则next[3]=0;,若最后一个元素为B,则next[3]=j+1=1+1=2。

void computeNext(const char *pa,int next[])
{
	next[0]=0;
	int L=strlen(pa);
	int i=1;//字符串下标从1开始迭代
	int pre=0;//前一位元素next[]最长前后缀长度
	for(i,pre;i<L;i++)//遍历整个模式
	{
		while(pre>0&&pa[i]!=pa[pre])//寻找能够与当前i匹配上的pre位置
		pre=next[pre-1];
		if(pa[i]==pa[pre])pre++;
		next[i]=pre;
	 } 
}
KMP代码如下:
class Solution{
public:
	const char* kmp(const char*str,const char*pa)
	{
		const char* cur=str;
		const char* re=cur;
		const char* p=pa;
		while(*(++p))
		re++;
		int i;//已经匹配的长度 
		int j;//下次移动的步长 
                int idx=0;//从第一个字符开始匹配
		while(*re)
		{
			i=next[idx];
                        p=pa+i;;
                        cur=cur+i;
                       while(*cur==*p)
			{
				cur++;
				p++;
				i++;
			}
			if(*p=='\0')return cur;
			else
			{
				idx=i;
                                j=i-next[idx];
				cur=cur+j;
				re=re+j;
			}
		}
		return NULL;	
	}
private:
    void computeNext(const char pa[],int next[])
{
	next[0]=0;
	int L=strlen(pa);
	int i=1;//字符串下标从1开始迭代
	int pre=0;//前一位元素next[]最长前后缀长度
	for(i,pre;i<L;i++)//遍历整个模式
	{
		while(pre>0&&pa[i]!=pa[pre])
		pre=next[pre-1];
		if(pa[i]==pa[pre])pre++;
		next[i]=pre;
	 } 
}	
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值