模式匹配算法

模式匹配:

定义:主串s或者称作目标串,是匹配的的对象。而子串或者称作模式串t,是能在主串s中找到的一个与t相等的子串,实际上不属于主串s。这样称模式串t定位匹配主串的过程称作模式匹配。

相关算法:

1、BF算法

即一个个比较。选择主串s,从第一个元素开始与模式串t第一个元素开始比较,如果相等则比较下一个;否则从主串第二个元素开始与模式串第一个字符比较。依次进行。

int bf_index(sqstr s,sqstr t){
int i=0,j,k;
if(t.len==0)return -1;
while(i<s.len)
{	k=i;
 for(j=0;j<t.len;j++)
 { if(k<s.len&&s.a[k]==t.a[j]){k++;continue;}
	else break;}
 if(j==t.len)return i;//比较完
i++;}
return -1;
	}

上面是借助另一个参数k来存储主串下标变化,如果仅仅用i,j两个参数逐次扫描s和t,需要在不匹配时进行回溯:i=i-j+1,j=0

int bf_index1(sqstr s,sqstr t){
int i=0,j=0;
if(t.len==0)return -1;
while(i<s.len&&j<t.len){
	if(s.a[i]==t.a[j]){i++;j++;}
	else {i=i-j+1;
	      j=0;}
  }
if(j==t.len)return i-t.len;
else return -1;
}

2、KMP算法

该算法利用模式串t中隐含的有利于模式匹配的信息即部分匹配信息:

对于模式串t[i],存在一个整数k(k<i),有t[0]t[1]...t[k-1]与t[i]之前的k个字符t[i-k]t[i-k+1]...t[i-1]相等,例如t="abcac",用next数组存放部分匹配信息。对于第一个字符a,规定next[0]=-1;对于第二个b,规定next[1]=0;对于第三个c,其前面只有字符b,与模式t开头字符不等即next[2]=0;第四个字符是a,其前面包括bc和c,和模式t开头字符不等即next[3]=0;第五个字符c,其前面包含bca,ca,a,其中a与t的开头字符相等故next[4]=1.

i01234
t[i]abcac
next[i]-10001
代码实现:

void getnext(sqstr s,int n[]){
int i=0,k=-1;
n[i]=-1;
while(i<s.len-1){
  if(k==-1||s.a[i]==s.a[k]){i++;k++;n[i]=k;}
else k=n[k];}
}
KMP算法匹配过程如下:

例如:s="ababcabcacbab"

            t="abcac"


故匹配KMP算法:

int kmp_index(sqstr s,sqstr t){
int next[N];
getnext(t,next);
int i=0,j=0;
while(i<s.len&&j<t.len){
	if(j==-1||s.a[i]==t.a[j]){i++;j++;}
	else {j=next[j];}
}
if(j==t.len)return i-t.len;
else return -1;
}
改进算法:

对于下面情况:


当i=3,j=3时,s[i]!=t[j],且按照上面KMP算法,需要比较(i=3,j=2),(i=3,j=1),(i=3,j=0)三次,还是不等需要再i++,j++。这样效率就差了!因此提出改进。

实际上,模式串t前3个字符与第四个字符都相等,而s与t不相等处正好是第四个字符,因此可以直接将模式串t右移4位直接开始i=4,j=0的比较。即不相等时按照next数组得到next[j]=k,当t[k]=t[j]且s[i]!=t[k]时候实际上是不需要在对t[k]进行比较的,而应该和t[next[k]]比较。故改进next数组:

if(t[j]==t[k])next1[j]=next1[k]

else next[j]=next[j];

j01234
t[j]abcac
k=next[j]-10001
next1[j]-100-10

代码实现:

void getnext1(sqstr s,int n1[]){
int i=0,k=-1;
n1[i]=-1;
while(i<s.len-1){
	if(k==-1||s.a[i]==s.a[k]){i++;k++;n1[i]=n1[k];}
	else k=n1[i];}
}

下面对BF算法应用:

如找到顺序串中最长重复字符串:

对于当前字符ai,判断之后aj(j>=i+1)是否存在a[i]==a[j],若相等则再比较a[i+1],a[i+2]...与a[j+1],a[j+1]...之间是否相等。直到找到不等,记录此时相等长度len1。再与原长度len比较。

sqstr same(sqstr s){
int len=0,index=0;
int len1,index1;
int i=0;
int j,k;
sqstr ss;
init(ss);
while(i<s.len)
{
for(j=i+1;j<s.len;j++)
	if(s.a[i]==s.a[j])
	{len1=1;
for(k=1;s.a[i+k]==s.a[j+k];k++)len1++;
if(len1>len){index=i;len=len1;}
    j+=len1;
     }
	else continue;
i++;}
ss.len=len;
for(index1=0;index1<len;index1++)
	ss.a[index1]=s.a[index+index1];
return ss;
}

求顺序串中第一个最长重复串,类似上面,只不过是不同串:

sqstr getsame(sqstr s,sqstr t){
	int i=0,j=0,k;
	int len=0,index=0;
	int len1;
	sqstr ss;
init(ss);
	while(i<s.len)
	{   j=0;
		while(j<t.len)
		{if(s.a[i]==t.a[j])
		{  len1=1;
		for(k=1;s.a[i+k]==t.a[j+k];k++)len1++;
		if(len1>len){index=i;len=len1;}
		j+=len1;}
		else j++;
		}
		i++;
	}
ss.len=len;
for(k=0;k<len;k++)
	ss.a[k]=s.a[index+k];
return ss;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值