朴素的字符串匹配算法

一个字符串是一个定义在有限字母表∑上的字符序列。例如,ABCDABC是字母表∑ = {A,BC,D}上的一个字符串。字符串匹配问题就是在一个大的字符串T中搜索某个字符串P的所有出现位置。其中,T称为文本(或称主串,模式串),P称为模式(或称子串),T和P都定义在同一个字母表∑上。

设文本为长度为n,用字符数组T[1..n]表示,模式串长度为m,m<=n,用字符数组P[1..m] 表示。

如果T[s+1..s+m] = P[1..m](即对1<=j<=m,有T[s+j]=P[j]),则说模式P在文本T中出现且位移为s。主串中共含有n-m+1个长度为m的子串,显然有0<=s<=n-m。如果模式P在文本T中出现并且位移为s,则称s是一个有效位移,否则称s为无效位移。所以字符串匹配问题就变成了在一段指定的文本T中,查找模式P出现的所有有效位移问题。

算法思想:把文本T中的n-m+1个模式分别和模式P进行比较,也就是说对位移s的n-m+1个可能的取值,依次比较两个子串,如果匹配,则s为一个有效位移,输出s。

算法伪代码:

Native-String-Matcher(T,P)
{
n = length[T];
m = length[P];
for s=0 to n-m
if(T[s+1..s+m] = P[1..m])
print”Pattern occurs with shift”s
}


算法分析:

从伪代码可知,比较两个长度为m的子串,复杂度为O(m),而外层循环n-m+1次,所以本算法的运行时间O((n-m+1)m)

一个简单的C++实现如下:

void Native_String_Matcher(const char* T, const char* P)
{
	int n = strlen(T);
	int m = strlen(P);

	for(int s=0; s<=n-m; s++)
	{
		if(strncmp(T+s,P,m) == 0)//比较m个字符
		{
			cout<<"Pattern occurs with shift"<<s<<endl;
		}
	}
}


另一个简单C++实现如下:

/*
  Description:在文本T中,从下标pos处开始查找模式P
  Return:若找到,则返回第一次匹配的(首字符)下标位置 ,否则返回-1; 
*/
int index(const char* T, const char* P, int pos)
{
	int n = strlen(T);
	int m = strlen(P);
	int i = pos;
	int j= 0;
	while(i<n && j<m)
	{
		if(T[i]==P[j])
		{
			i++;
			j++;
		}
		else
		{
			i = i-j+1;	//匹配失败,指针回溯  
			j = 0;
		}
	}
	if(j==m)
		return i-m;
	return -1;
}

这个实现可能不如第一个实现那么清晰,但是本质上是一样的。该程序初始化i=pos,j=0,然后开始逐一比较对应字符值,若相等则指针i和j都向后移动,若不相等则指针回溯。i=i-j+1;也就是把本次扫描前指针i的位置(通过此时i的值减去移动的值j求得)向后移动一位,相当于把模式向右移一位。

循环结束时可能有两种情况:(1)j==m,也就是说找到了第一个匹配的子串,我们返回下标值就可以。(2)i==n且j!=m,也就是说查找完了文本,但是没有找到匹配的子串,出现此种情况是因为当位移s> n-m的时候,文本中剩余的子串长度不足m。


测试程序如下:

#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;

int main(int argc, char *argv[])
{
	const int Max_Length = 1000;
	char T[Max_Length];
	char P[Max_Length];
	while(gets(T))
	{
		gets(P);
		//Native_String_Matcher(T,P);
		//cout<<"next case:"<<endl;
		cout<<"first match at "<<index(T,P,0)<<endl;
	}
    system("PAUSE");
    return EXIT_SUCCESS;
}

注:

(1)所有程序都是在DEV-C++上测试通过的,除非特别说明。

(2)由于使用的是C的字符输入函数gets(),输入时文本和模式应该以回车符作为分隔。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值