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

字符串匹配(查找)算法是一類重要的字符串算法,給定一個長度爲n的字符串text,要求找出text中是否存在另一個長度爲m的字符串pattern(模式串)。
主要方法有樸素算法,KMP算法,BM(Boyer Moore)算法,Sunday算法。
樸素算法就是暴力匹配,KMP非常有名,但效率感人,不多說。
關於Boyer-Moore算法Sunday算法《字符串匹配的Boyer-Moore算法》《字符串匹配算法之Sunday算法》兩篇文章講得很清楚。
《字符串匹配算法之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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章