一种基于Sunday算法的单模式字符串匹配算法

最近看了几个字符串匹配算法,偶然想到对Sunday算法的一种可能的改进(改变)

问题描述:

单模式字符串匹配

算法综述:

Sundy算法四目前最快的字符串匹配算法,本文在sunday基础上做了一些改进(或者改变),能在一定程度上提高sunday算法的运行效率,较有限,但也算是一种思路。

Sunday算法:

从前往后匹配,每次匹配不成功,关注的是尾部的后一字符(设该字符为x)是否在模式串中出现。若出现,移动模式串,使x与模式串中最后一次出现的该字符(下标最大)对齐;若没出现过,移动模式串使其头字符与x的下一字符对齐。总体思想是尽可能大步长地往后移动模式串。

本文新提出SundayEvening算法:

总体思想依然是尽可能大步长后移模式串。SundayEvening算法每次从后往前匹配,每次匹配不成功,不只关注尾部后一字符(设为x),也关注当前匹配不成功的字符(设为y)。因为:当尾x在模式串中,而y不在模式传中时,sunday算法可能没有找到最大移动步长。

SundayEvening的做法是,每当y匹配不成功,若y在模式串中则计算移动模式串使其相应字符与y对齐时的步长,若y不在字符串中,计算移动模式串使其头部与y后一字符对齐时的步长,总之,可以计算出按y来移动模式串的步长。再来看x,当x不在字符串中时,操作与sunday算法一致,若x在字符串中,计算按x移动的步长,与上述按y移动的步长比较,取较大的来进行移动。这样就尽可能的获得了较大的移动步长。而SundayEvening从后往前匹配显然也有助于获得更大步长(y靠后的概率大)。

Sunday和SundayEvening的比较:

SundayEvening算法通过两种措施①从后向前匹配②比较步长选较大者,提高了获得最大步长的可能性。但是会增加一些比较操作。当模式串长度极大或极小时,效率与sunday基本无差别,但是在中间某一区域内,SundayEvening算法可以获得约百分10到百分之15的效率提升。(测试数据为随机字符串,字符范围为ascii码中96个可打印字符)。

代码实现:

代码中写了三个算法:Sunday、KMP、SundayEvening(为了比较,将KMP一同写出)

测试数据:随机字符串(字符范围为96个可打印字符)

测试串长度:1000000000

模式串长度:10

测试结果与模式串长度、文本串长度、字符范围均有关系,文本串较小或模式串较大时效率与Sunday算法基本无差(但貌似不会低于Sunday)。

一次测试的结果:效率提升百分之14

具体代码:

 

#pragma once
#include<iostream>
#include<Windows.h>
#include<stdlib.h>
#include<time.h>
using namespace std;
#define random(x) (rand()%x) 

//*********************KMP算法:***********************

void getNext(const unsigned char shortList[], int next[], int shortLength)
{
	next[0] = -1;
	int pointer = -1;//初始值是前面子串的前缀长度(前面子串前缀后第一个元素位置)
	//pointer最终指向添加shortList[index]之前的子串的最长前缀后的第一个元素
	int index = 0;//next数组指针,从1开始为next[index]赋值
	while (index <= shortLength - 1)
	{
		if (pointer == -1 || shortList[index] == shortList[pointer])
		{
			pointer++;//前缀长度++(位置后移一位)
			index++;
			//if (shortList[index] == shortList[pointer])//index不匹配,若pionter位置与他相同,也一定不匹配,应避免这种pointer
			//	next[index] = next[pointer];//从头开始,每个index都不与他对应的pointer位置元素相同,所以若出现相同,只需一次向前迭代即可保证不同了
			//else
			next[index] = pointer;//不同,不匹配就跳到pointer位置看shortList[pointer]是否匹配
		}
		else
			//从未加shortList[index]之前的子串的前缀中,寻找更小的前缀(前缀的前缀),该前缀与后缀的后缀相同,然后看能不能匹配
			pointer = next[pointer];
	}
}

int KMP(const unsigned char longList[], const unsigned char shortList[], int next[], long  longLength, int shortLength)
{
	int lp = 0;//longList中的指针
	int sp = 0;//shortList中的指针
	while (lp < longLength && sp < shortLength)
	{
		if (sp == -1 || longList[lp] == shortList[sp])//都不匹配重新检查,或者匹配
		{
			lp++;
			sp++;
		}
		else//取前面与 #lp之前子串的某后缀# 相同的某前缀后的第一个元素
		{
			sp = next[sp];
		}
	}
	if (sp == shortLength)
		return(lp - shortLength);
	else
		return -1;
}

/
/

//*********************Suday算法:***********************

void GetMaxIndex(int maxindex[], const unsigned char* shortList, const int shortLength)
{
	for (int i = 0; i < 256; i++)
		maxindex[i] = -1;
	for (int i = 0; i < shortLength; i++)
	{
		if (maxindex[shortList[i]] < i)
			maxindex[shortList[i]] = i;
	}
}

int Sunday(const unsigned char shortList[], const unsigned char longList[], int maxindex[], const int shortLength, const long  longLength)
{
	int lp = 0;//longList指针
	int sp = 0;//shortList指针
	int lhead = lp - sp;//longList中与shortList首元素对齐的元素下标
	int lrear = lhead + shortLength - 1;//longList中与shortList尾元素对齐的元素下标
	int result = -1;//longList中shortList的位置
	while (lrear < longLength)
	{
		if (shortList[sp] == longList[lp])
		{
			if (lp == lrear)
				return lhead;
			lp++;
			sp++;
		}
		else if (lrear + 1 >= longLength)//检查到末尾了,跳出循环
			break;
		else if (maxindex[longList[lrear + 1]] == -1)//后一字符不在shortList中
		{
			lp = lrear + 2;
			sp = 0;
			lhead = lp;
			lrear = lhead + shortLength - 1;
		}
		else//后一字符串在shortList中
		{
			lhead = lrear + 1 - maxindex[longList[lrear + 1]];
			lrear = lhead + shortLength - 1;
			lp = lhead;
			sp = 0;
		}
	}
	return result;
}


/
//***********************Sunday改进算法*************************

int SundayEvening(const unsigned char shortList[], const unsigned char longList[], int maxindex[], const int shortLength, const long  longLength)
{

	int lhead = 0;//longList中与shortList首元素对齐的元素下标
	int lrear = shortLength - 1;//longList中与shortList尾元素对齐的元素下标
	int lp = lrear;//longList指针
	int sp = shortLength - 1;//shortList指针
	int result = -1;//longList中shortList的位置
	int step = 0;//前移的数目
	while (lrear < longLength)
	{
		if (shortList[sp] == longList[lp])
		{
			if (lp == lhead)
				return lhead;
			lp--;
			sp--;
		}
		else if (lrear + 1 >= longLength)//检查到末尾了,跳出循环
			break;
		else if (maxindex[longList[lrear + 1]] == -1)//后一字符不在shortList中
		{
			lhead = lrear + 2;
			lrear = lhead + shortLength - 1;
			sp = shortLength - 1;
			lp = lrear;
		}
		else
		{
			if (lrear + 1 - maxindex[longList[lrear + 1]] < lp - lhead + 1)
			{
				lhead += lp - lhead + 1;
				lrear = lhead + shortLength - 1;
				lp = lrear;
				sp = shortLength - 1;
			}
			else
			{
				lhead = lrear + 1 - maxindex[longList[lrear + 1]];
				lrear = lhead + shortLength - 1;
				lp = lrear;
				sp = shortLength - 1;
			}

		}
	}
	return result;
}




void main()
{
	srand((int)time(0));

	int shortLength = 10;
	long longLength = 1000000000;
	unsigned char* shortList = new unsigned char[shortLength];
	unsigned char* longList = new unsigned char[longLength];
	for (long i = 0; i < shortLength; i++)
	{
		int a = random(96);
		unsigned char c = a;
		shortList[i] = c;
	}
	for (long i = 0; i < longLength - shortLength; i++)
	{
		char c = random(96);
		longList[i] = c;
	}
	for (long i = 0; i < shortLength; i++)
		longList[i + longLength - shortLength] = shortList[i];

	DWORD start_time = GetTickCount();

	int maxindex[256];//存shortList中出现有的字符在shortList中最后一次出现的位置,同时也标记那些字符出现在了shortList中
	GetMaxIndex(maxindex, shortList, shortLength);
	int Sunday_result = Sunday(shortList, longList, maxindex, shortLength, longLength);
	DWORD Sunday_end_time = GetTickCount();

	if (Sunday_result < 0)
		cout << "未在文本串中找到模式串!" << endl;
	else
		cout << "模式串在文本串中的起始下标为:" << Sunday_result << endl;

	cout << "The Sunday run time is:" << (Sunday_end_time - start_time) << "ms!" << endl;//输出运行时间

	int *next = new int[shortLength];
	//next[index]存index前面子串中前缀后的第一个元素位置,不匹配时换这个位置的元素看是否匹配
	//next是对应子串最长前缀长度数组的右移(左端补-1)
	getNext(shortList, next, shortLength);
	int KMP_result = KMP(longList, shortList, next, longLength, shortLength);
	if (KMP_result < 0)
		cout << "未在文本串中找到模式串!" << endl;
	else
		cout << "模式串在文本串中的起始下标为:" << KMP_result << endl;


	DWORD KMP_end_time = GetTickCount();
	cout << "The KMP run time is:" << (KMP_end_time - Sunday_end_time) << "ms!" << endl;//输出运行时间

	GetMaxIndex(maxindex, shortList, shortLength);
	int Sunday2_result = SundayEvening(shortList, longList, maxindex, shortLength, longLength);
	DWORD Sunday2_end_time = GetTickCount();

	if (Sunday2_result < 0)
		cout << "未在文本串中找到模式串!" << endl;
	else
		cout << "模式串在文本串中的起始下标为:" << Sunday2_result << endl;

	cout << "The SundayEvening run time is:" << (Sunday2_end_time - KMP_end_time) << "ms!" << endl;//输出运行时间

	cout << "提高了:" << 1 - (double)(Sunday2_end_time - KMP_end_time) / (Sunday_end_time - start_time) << endl;

	system("pause");
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值