单字符串匹配算法(Boyer Moore和Sunday)

本文对比了string::find、BM(BoyerMoore)和Sunday三种字符串匹配算法的性能,发现pattern较小时,string::find效率最高,随pattern增大,BM和string::find效率接近,而Sunday算法效率显著提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串匹配(查找)算法是一类重要的字符串算法,给定一个长度为n的字符串text,要求找出text中是否存在另一个长度为m的字符串pattern(模式串)。
主要方法有朴素算法,KMP算法,BM(Boyer Moore)算法,Sunday算法。
朴素算法就是暴力匹配,KMP非常有名,但效率感人,不多说。
关于Boyer-Moore算法Sunday算法《字符串匹配的Boyer-Moore算法》《字符串匹配算法之Sunday算法》两篇文章讲得很清楚。
《Simple、KMP、BM、Sunday匹配算法-效率测试》对它们的效率进行了分析。

我实现了BM和Sunday算法,并同string::find做了对比,O3优化,测试发现pattern字符串比较小的时候,string::find优势巨大,比BM和Sunday高效数倍,pattern增大的过程中,BM渐渐和string::find相当了(但效率还是稍低),Sunday变得优秀了,花费的时间是string::find的37%左右。(注:1. Sunday始终比BM效率高 2.启不启用“好后缀”对BM影响不大)

为什么和理论分析不太一样呢?我猜测原因可能有:string::find可能并不是暴力匹配;标准库有优化;我的实现不够好;测试数据有问题;pattern字符串小的时候,BM和Sunday额外的操作带来的效率提升不如花费的成本。

结论:大部分时候string::find足够了,如有需要可以用Sunday算法。
BM算法比Sunday算法复杂的多,实现BM用了半天,实现Sunday只用了不到一个小时。简单可能更好。

附上测试代码:

#include <iostream>
#include <string>
#include <vector>

#include "boyermoore.h"
#include "sunday.h"

using std::string;
using std::cout;
using std::endl;
unsigned char table[]=
{
	'0','1','2','3','4','5','6','7','8','9','<','>',' ',
	'A','B','C','D','E','F','G','H','I','J','K','L','M',
	'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
	'a','b','c','d','e','f','g','h','i','j','k','l','m',
	'n','o','p','q','r','s','t','u','v','w','x','y','z'
};
class FindStr
{
	std::vector<string> strs;
public:
	void make_strs(int min_len, int max_len, int count)
	{
		srand(99999);
		for(int i=0; i<count; ++i)
		{
			int len = rand() % (max_len - min_len) + min_len;
			string str(len, 0);
			for(int j=0; j<len; ++j)
				str[j] = table[rand() % sizeof(table)];
			strs.push_back(str);
		}
	}
	int search1(const string &str)
	{
		int ret_c = 0;
		for(const auto &i:strs)
		{
			if(i.find(str) != string::npos) ++ret_c;
		}
		return ret_c;
	}
	int search2(const string &str)
	{
		BoyerMoore bm(str.c_str());
		int ret_c = 0;
		for(const auto &i:strs)
		{
			if(bm.findin(i.c_str())[0]) ++ret_c;
		}
		return ret_c;
	}
	int search3(const string &str)
	{
		Sunday s(str.c_str());
		int ret_c = 0;
		for(const auto &i:strs)
		{
			if(s.findin(i.c_str())[0]) ++ret_c;
		}
		return ret_c;
	}
};

#include <unistd.h>
#include <sys/time.h>
int main()
{
	FindStr s;
	s.make_strs(5000, 9800, 10000);

	string pattern("p4rDtntwBOohZzovBXPIvmQyAlW3BsV1aY3tUofJRt9T3es1aj4UKG0Da04m6F0Qkyem6KzoPucNMU7gyPoXDpbKCYyiqXZSIJkHhOi3719vNCsOyJdofOYinSJxhWNZjVvuBME92OfYCqxKPjtLiNd1U2Aeqfpuq2LWaaw9iIFUEnMVesydIX4Q1Lj5uJKq91JPHht4z5EZ4OidE3RhdcIoyVFsbhwXDSeCQVFilps5oaVY7SfHEwnQcWffR7eT4dqV1cvSJ6jqzaWA1jGlpirfIvSzyn9v6ZNP7nyagxzs");
	struct timeval start, stop;
	gettimeofday(&start, nullptr);
	cout << "find " << s.search1(pattern.c_str()) << endl;
	gettimeofday(&stop, nullptr);
	cout << "search1 using " << stop.tv_sec*1000 + stop.tv_usec/1000 - start.tv_sec*1000 - start.tv_usec/1000 << "ms" << endl;
	gettimeofday(&start, nullptr);
	cout << "find " << s.search2(pattern.c_str()) << endl;
	gettimeofday(&stop, nullptr);
	cout << "search2 using " << stop.tv_sec*1000 + stop.tv_usec/1000 - start.tv_sec*1000 - start.tv_usec/1000 << "ms" << endl;
	gettimeofday(&start, nullptr);
	cout << "find " << s.search3(pattern.c_str()) << endl;
	gettimeofday(&stop, nullptr);
	cout << "search3 using " << stop.tv_sec*1000 + stop.tv_usec/1000 - start.tv_sec*1000 - start.tv_usec/1000 << "ms" << endl;

	return 0;
}

find 0
search1 using 76ms
find 0
search2 using 82ms
find 0
search3 using 29ms
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值