Boyer-Moore算法的C++实现

2023-05-16

 BM算法-阮一峰的网络日志;

 以上给出了通俗易懂的算法讲解,下面给出代码实现,使用的宽字符,这样就不限于英文字母了:(stdafx.h编译不过去就屏蔽掉)

// StringSearch_BoyerMoore.cpp : 定义控制台应用程序的入口点。
//
/*
	@Date: 2016/03/21 18:31;
	@Where: Tongzhou District of Beijing;
	@Author: Alex Hong;
	@Mail: worksdata@163.com;
	@Reference: 
		http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html BM算法-阮一峰的网络日志;
*/

#include "stdafx.h"
#include <sstream>
#include <iostream> 
#include <string>
#include <vector>
#include <map>
using namespace std;

typedef std::vector<int> TArrayInt;
TArrayInt g_arSearchResult;//store the index of searched result;
typedef std::map<std::wstring, int> TMapWszToInt; //E.g. "我,4":7;
typedef std::map<int, int> TMapIntToInt;


void StringSearch_BM(wchar_t* wszStrMain, int iLenMain, wchar_t* wszSearch, int iLenSearch, bool bOnlyFirst)
{
	//reset variables;
	g_arSearchResult.clear();

	//handle exceptions..
	if( NULL == wszStrMain || NULL == wszSearch)
		return;
	if(iLenSearch > iLenMain)
		return;
	/*
		未知的字符有很多, 但是已知的搜索字符是有限的. 我们只初始化已知的字符的跳跃值(Skip), 数组中不存在的就是-1;
		Shift数组的初始化同样基于这种想法.
		初始化这两个数组的值后, 对于长主串/耗时的任务是有利的;
	*/

	int i = 0, j = 0;

	//Initialize the Skip array data for Bad-Character-Rules..
	TMapWszToInt mapSkip;
	mapSkip.clear();
	TMapWszToInt::iterator it1;
	std::wostringstream oStream;
	for(i = iLenSearch - 1; i > 0; --i)//0-->1;
	{
		for(j = i - 1; j >= 0; --j)
		{
			if(wszSearch[j] != wszSearch[i]) //Skip is for bad character which is different with wszSearch;
			{
				oStream.clear();//clear bad bits;
				oStream.str(L"");//clear data;
				oStream<<wszSearch[j]<<','<<i;//format to key string;
				//wcout<<oStream.str().c_str()<<endl;//test..
				it1 = mapSkip.find(oStream.str());
				if(mapSkip.end() == it1) //only set the pos of nearest same bad character;
					mapSkip.insert(TMapWszToInt::value_type(oStream.str(), i-j));
			}
		}
	}

	//好后缀的前提是: 至少有一个字符是相同的!!!!!!
	//Initialize the Shift array data for Good-Suffix-Rules..
	//Shift数组的键值是好后缀的最左字符的下标;
	//1. 首先判断模式串中是否含有完全一致的好后缀;有就直接计算位移, EXIT;
	//  其实这本身就是一个字符串搜索的过程,为了简化计算, 这里只使用Bad-Character-Rule;
	//2. 如果没有, 就在串首寻找好后缀的子串;
	TMapIntToInt mapShift;	
	mapShift.clear();
	mapShift.insert(TMapIntToInt::value_type(0, iLenSearch));//0==i的话就匹配了, Shift[]=iLenSearch;
	//1. try to find the same suffix..
	for(int iSuffixPosL = iLenSearch - 1; iSuffixPosL > 0; --iSuffixPosL)//iSuffixPosL: left position of good suffix;
	{
		int iSuffixLen = iLenSearch - iSuffixPosL;
		wchar_t* wszSuffix = wszSearch + iSuffixPosL;
		
		for(int iPosMainL = iSuffixPosL - 1; iPosMainL >= 0;)//不断向左移动suffix串比较;
		{
			//compare..
			for(i = 0; i < iSuffixLen; ++i)
			{
				if(wszSearch[iPosMainL+i] != wszSuffix[i])//bad character
					break;
			}
			//check result..
			if(iSuffixLen == i) //find it!
			{				
				mapShift[iLenSearch-i] = iSuffixPosL - iPosMainL; 
				break; //结束为当前suffix找Shift值;
			}
			else //calc the skip distance;
			{
				for(j = i + 1; j < iSuffixLen; ++j)//在suffix中寻找最近的坏字符
				{
					if(wszSuffix[j] == wszSearch[iPosMainL+i])
						break;
				}
				iPosMainL = iPosMainL - (j - i);//iSuffixLen==j的情况也适用;	
			}
		}
	}
	//2. try to find the child suffix at head..
	TMapIntToInt::iterator it2;
	for(int iSuffixPosL = iLenSearch - 1; iSuffixPosL > 0; --iSuffixPosL)//iSuffixPosL: left position of good suffix;
	{
		it2 = mapShift.find(iSuffixPosL);
		if(mapShift.end() == it2)
		{
			wchar_t* wszSuffix = NULL;
			int iSuffixLen  = 0;
			for(i = 0; i < iLenSearch - iSuffixPosL; ++i)//form sub suffix big to small.
			{
				wszSuffix = wszSearch + iSuffixPosL + i;
				iSuffixLen = iLenSearch - iSuffixPosL - i;
				//compare if match..				
				for(j = 0; j < iSuffixLen; ++j)
				{
					if(wszSearch[j] != wszSuffix[j])
						break;
				}
				if(iSuffixLen == j) //find it!
				{
					mapShift[iSuffixPosL] = iLenSearch - iSuffixLen; //以好后缀的最后一个字符为准;
					break;
				}
			}

			//if no sub suffix at all..
			it2 = mapShift.find(iSuffixPosL);
			if(mapShift.end() == it2)
				mapShift[iSuffixPosL] = iLenSearch;
		}
	}

	//================
	//start to search!
	//================
	int iPosMainL = 0;//left iterator of main string;
	//int iPosSearch = 0;//iterator of search string; no use!
	while( iPosMainL <= iLenMain - iLenSearch + 1)
	{
		//compare with aligned sub-string..
		int i = iLenSearch - 1;
		for(; i >= 0; --i)//right to left comparison;
		{
			if(wszSearch[i] != wszStrMain[iPosMainL+i])//bad character!
				break;
		}
		//----calc how many steps to move rightwards..----
		int iSkip = 0;
		if(i < 0)
		{
			g_arSearchResult.push_back(iPosMainL);
			if(bOnlyFirst)
			{
				mapSkip.clear();
				mapShift.clear();
				return;
			}
			iSkip = iLenSearch;
		}
		else
		{
			//1. calc max skip steps for bad characters..
			oStream.clear();//clear bits;
			oStream.str(L"");//clear data;
			oStream<<wszStrMain[iPosMainL+i]<<','<<i;
			it1 = mapSkip.find((wchar_t*)oStream.str().c_str());
			if(mapSkip.end() == it1)
				iSkip = i + 1;
			else
				iSkip = it1->second;
		}
		
		//--2. calc max shift steps for good suffix..--
		int iShift = 0;
		if(i < iLenSearch - 1)//至少有一个相同的字符才可以使用"好后缀"规则!
		{
			it2 = mapShift.find(i-1);
			if(mapShift.end() != it2)
				iShift = it2->second;
		}
		iPosMainL += ((iSkip>iShift) ? iSkip : iShift);
	}

	//clear..
	mapSkip.clear();
	mapShift.clear();
}

int _tmain(int argc, _TCHAR* argv[])
{
	wchar_t wszMainStr[] = L"THIS IS A 我你她我不是的LE EXA我们我们MPLEEXAMPLE我们";
	wchar_t wszSearchStr[] = L"我们";

	StringSearch_BM(wszMainStr, wcslen(wszMainStr), wszSearchStr, wcslen(wszSearchStr), false);

	for(TArrayInt::iterator it = g_arSearchResult.begin(); it != g_arSearchResult.end(); ++it)
		cout<<*it<<endl;

	//clear..
	g_arSearchResult.clear();

	getchar();
	return 0;
}

 一些例子分析过程:

主字符串:       THIS IS A SIMPLE EXAMPLE  (长度:24)
搜索字符串:    EXAMPLE                            (长度:7)

    
mapSkip的初始化值:

主串中的坏字符的下面字符是:'E'

  mapSkip['L,6'] = 1
  mapSkip['P,6'] = 2
  mapSkip['M,6'] = 3
  mapSkip['A,6'] = 4
  mapSkip['X,6'] = 5
                             mapSkip['E,6'] = 不是坏字符, 舍去
  其他: 7
-----------------------------------------------------------
搜索字符串:    EXAMPLE                   (长度:7)
主串中的坏字符的下面字符是:
               'L'
  mapSkip['P,5'] = 1
  mapSkip['M,5'] = 2
  mapSkip['A,5'] = 3
  mapSkip['X,5'] = 4
  mapSkip['E,5'] = 5
  其他:6
---------------------------------------------------------------
...
主字符串:       THIS IS A SIMPLE EXAMPLE  (长度:24)
搜索字符串:    EXAMPLE                           (长度:7)
主串中的坏字符的下面字符是:
               'X'
  mapSkip['E,1'] = 1
  其他:2
---------------------------------------------------------------
 mapSkip['[任何],0'] = 1

============好后缀数组初始化的流程============================

搜索字符串:    SIMPLE EXAMPLE  (长度:14)

0  1  2  3  4  5  6  7  8  9  10  11  12  13
S  I  M  P  L   E     E  X  A   M   P    L    E

好后缀的第一个字符为:'E' --> Shift[13] = 6; //以最后一个字符的下标为基准计算要移动的距离;
好后缀的第一个字符为:'L' --> Shift[12] = 8; //"SIMPLE"中有全匹配的, 取全匹配的;
好后缀的第一个字符为:'P' --> Shift[11] = 8;
好后缀的第一个字符为:'M' --> Shift[10] = 8;
好后缀的第一个字符为:'A' --> Shift[9]  = 14; //没有全匹配的, 在串头部找子串匹配的, 这里没有, 认为在-1位置(不妨虚拟出来);
...
好后缀的第一个字符为:'I' --> Shift[1] = 14;
好后缀的第一个字符为:'S' --> Shift[0] = 14;//可能需要特殊处理;


E.g. 2

0  1  2  3   4   5   6   7
L  E  X  A   M   P   L   E

好后缀的第一个字符为:'E' --> Shift[7] = 6;
好后缀的第一个字符为:'L' --> Shift[6] = 6;
好后缀的第一个字符为:'P' --> Shift[6] = 6;

转载于:https://www.cnblogs.com/tupx/p/5337145.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boyer-Moore算法的C++实现 的相关文章

  • 基于ArUco的视觉定位

    参考如下 博客 基于ArUco的视觉定位 1 3 https www freesion com article 4265319144 基于ArUco的视觉定位 4 https www pianshen com article 2491452
  • 伺服电机和步进电机的区别

    硬件型号 xff1a 三菱伺服电机HG KR43J 系统版本 xff1a 电机系统 1 控制的方式不同 步进电机 xff1a 通过控制脉冲的个数控制转动角度的 xff0c 一个脉冲对应一个步距角 伺服电机 xff1a 通过控制脉冲时间的长短
  • ubutnu更换国内源后,更新一直出现404,Not Found的问题

    1 问题 题主系统是ubuntu16 04 64位系统 尝试更换国内各种源 连ubuntu官方源都尝试了 sudo vim etc apt sources list修改为 deb https mirrors tuna tsinghua ed
  • Python+Flask实现股价查询系统。Python绘制股票k线走势

    文章目录 一 实现效果图二 实现思路1 获取数据 2 可视化数据三 源码获取 一 实现效果图 打开默认显示半年线 xff0c 可以通过可视化类型选择可视化k线图 高低点等 xff08 目前只完成了初版 xff0c 当查询的股票数据返回为空时
  • Failed to fetch http://mirrors.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/gcc-5/g++-5_5.4.0-6ubuntu1~16

    今天在ubutun中在安装redis过程中 xff0c 安装gcc时遇到了Failed to fetch http mirrors tuna tsinghua edu cn ubuntu pool main g gcc 5 g 43 43
  • 切换日语输入法找不到MicrosoftIME键盘选项了

    去微软官方下载一个 Microsoft IME office 2010后 xff0c 安装解决 转载于 https www cnblogs com tupx p 3816026 html
  • msgid 属性

    Android源码中的String xml文件 xff0c msgid这个属性是干嘛的 xff1f 全局资源 xff0c 方便引用 比如在布局的text和activity中用到 转载于 https www cnblogs com Ph on
  • 2017年09月23日普级组 数列

    Description 小S今天给你出了一道找规律题 xff0c 题目如下 xff1a 有如下的数列1 xff0c 11 xff0c 21 xff0c 1211 xff0c 111221 xff0c 312211 xff0c 小S问你这个数
  • python 机器学习实战:信用卡欺诈异常值检测

    今晚又实战了一个小案例 xff0c 把它总结出来 xff1a 有些人利用信用卡进行诈骗等活动 xff0c 如何根据用户的行为 xff0c 来判断该用户的信用卡账单涉嫌欺诈呢 xff1f 数据集见及链接 xff1a 在这个数据集中 xff0c
  • Virtual Serial Port Driver 虚拟串口工具软件 使用介绍

    一般来说 xff0c 电脑的外部设备可以用过各种端口和电脑连接 常见的有USB xff0c VGA xff0c DVI等等 在工业领域或者是软件开发领域 xff0c 我们常常需要用简单低成本快捷的方式 xff0c 完成电脑和设备的连接 那么
  • Freertos 源码分析 队列queue

    队列queue xff08 零 xff09 队列的基础概念和形态 xff08 一 xff09 Freertos 队列 queue c FreeRTOS Kernel 10 4 6 include queue h Freertos队列模块包含
  • Freertos 任务TASK(一) 任务创建

    任务的创建 Freertos 的任务创建难点 1 xff09 堆栈生长的方向 2 xff09 64字节的对齐 3 xff09 任务堆栈初始化 Freertos 的任务使用任务控制块来进行管理 xff0c 是对任务的抽象 任务本身就是一段可执
  • Freertos Cortex-M3上下文切换

    上下文切换是操作系统实现虚拟化的核心功能 xff0c 操作系统对任务的管理通过上下文切换完成 Freertos 在STM32F103上的上下文切换是本文介绍的内容 STM32F103 采用 Cortex M3 内核 上下文切换的本质是对现场
  • STM32CubeMX配置freertos配置任务(一)

    使用STM32CubeMX 配置Freertos 生成一个任务点亮LED stm32cubemx STM32CubeMX 是 ST 意法半导体近几年来大力推荐的STM32 芯片图形化配置工具 xff0c 允许用户使用图形化向导生成C 初始化
  • STM32 精准采集ADC电压,误差分析

    ADC模块采集电压流程 数字世界和模拟世界的桥梁 xff0c 对于嵌入式软件而言 xff0c 大家止于采集功能的实现 本文目的在于深入理解ADC xff0c 积累技术做出更加稳定优秀的产品 STM32 大部分系列都是使用SAR 逐次逼近型电
  • STM32 编码器驱动/旋转编码器旋钮encoder

    本文已比较纯粹的方式介绍编码器和驱动的编写 编码器最少有两个输出信号 xff0c 一种典型的结构如上图所示 AB是编码器的输出引脚 当触点和黄色的金属片接触的时候信号发生跳变沿 xff0c 可以上上升沿也可以是下降沿 xff0c 具体根据A
  • UTC 转 LocalTime

    使用unsigned const char 纯碎是为了配合项目 xff0c 改成char 会比较通用些 BOOL CDllSuiteEngine Time StrToType unsigned const char lpszValue SY
  • CubeMX 图形配置工具 (UART) H743

    本文分两部分 1 图形操作步骤 2 自动生成代码结构分析 3 自动代码生成的坑 一 xff1a 使用CubeMX 配STM32H743 串口模块LPUSART 1 使能调试口 xff0c 以免使用SWD下载需要手动复位 2 配置 时钟树 x
  • LWIP (1.1) ETH Module以太网模块

    STM32 以太网 ETH模块说明 1 overview 2 ETH module in stm32h743 STM32H743 为例 开局一张图 ETHER 模块 红框所示 以STM32H743为例 32 BIt AHB为内部高速总线 D

随机推荐

  • UML(二)component 组件图

    组件图即是用来描述组件与组件之间关系的一种UML图 组件图在宏观层面上显示了构成系统某一个特定方面的实现结构 组件图适用于基于组件的开发模式 xff08 Component Based Development CBD xff09 xff0c
  • xilinx ZYNQ 7000 AXI GPIO

    0AXI GPIO 第一部分 PS 和 PL之间的通讯有一个接口称为AXI AXI总线具体的内容这边不去深究 xff0c 可以理解为一种特殊协议的通讯方式 AXI GPIO是什么意思 xff1f PL是FPGA它可以做成任何你想要的东西 x
  • xilinx ZYNQ 7000 XADC 片上模拟转数字模块

    上图所示 xff0c XADC 属于 PL部分的资源 XADC是一种硬逻辑实现 xff0c 位于PL功率域 PS xadc接口是PS的一部分 xff0c 可以被PS APU访问 xff0c 而不需要对PL进行编程 PL必须上电才能配置PS
  • XILINX FPGA OV5640 摄像头驱动(一)

    影像行业是一个值得深耕的方向 xff0c 废话不多说 先看输入和输出 输入是光照 xff0c 输出是光照的数字信号 image area xff1a 说的是感光矩阵 xff0c CMOS图像传感器的最核心部分 xff0c 接收光照产生电信号
  • Xilinx ZYNQ 7000 HDMI

    High Definition Multimedia Interface HDMI 参考xilinx application note XAPP460 HDMI来自High Definition Multimedia Interface 高
  • CAN协议与CANOpen协议

    这里详细介绍了CAN协议中数据通信帧每位的含义 xff0c 有图片 xff0c 值得一看 xff1a https www cnblogs com pejoicen p 3986587 html 这里介绍了CanOpen协议 xff0c ht
  • 标准的 C++ 由三个重要部分组成

    标准的 C 43 43 由三个重要部分组成 xff1a 核心语言 xff0c 提供了所有构件块 xff0c 包括变量 数据类型和常量 xff0c 等等 C 43 43 标准库 xff0c 提供了大量的函数 xff0c 用于操作文件 字符串等
  • 【转】每个程序员应该阅读的10本经典书籍

    如果你是一个程序员 xff0c 除了编码之外 xff0c 你还需要大量的阅读 今天我要为大家介绍几本值得一读的书 xff0c 包括 The Pragmatic Programmer xff0c The Mythical Man month
  • 解决Sqlite Developer过期的最简单办法(转自百度经验)

    第一种方法是 xff1a 打开注册表 开始 gt 运行 gt 输入regedit 依次打开目录 HKEY CURRENT USER SharpPlus SqliteDev 找到右侧的StartDate项 xff0c 删除 第二种方法更简单
  • 使用PhpOffice的PhpWord生成Word文件损坏,提示:很抱歉,无法开test.docx,因为内容有问题The file is corrupt and cannot be opened

    先说一下我的环境 xff1a 客户端 xff1a 操作系统 xff1a Windows 10 专业版20H2 64 位 内部版本 xff1a 19042 870 浏览器 xff1a Microsoft Edge版本 89 0 774 75
  • Prometheus.yml配置文件示例

    my global config global scrape interval 15s Set the scrape interval to every 15 seconds Default is every 1 minute evalua
  • 如何查看chrome浏览器已保存的密码

    该方法是针对在chrome中已经存储了登陆密码的情况 chrome版本是 66 0 3359 139 xff08 正式版本 xff09 xff08 64 位 xff09 xff0c 不知道哪天会改了这个bug 一般来说 xff0c 我们登陆
  • SNMP学习笔记之iReasoning MIB Browser

    0x00 MIB Browser iReasoning MIB浏览器是一个强大和易于使用的工具由iReasoning SNMP API提供支持 MIB浏览器是工程师管理启用SNMP的网络设备和应用程序不可或缺的工具 它允许用户加载标准的 x
  • K8S学习笔记之将Google的gcr.io、k8s.gcr.io 换为国内镜像

    0x00 添加docker官方的国内镜像 sudo tee etc docker daemon json lt lt 39 EOF 39 34 registry mirrors 34 34 https registry docker cn
  • Docker学习笔记之常见 Dockerfile 使用技巧

    0x00 概述 在掌握 Dockerfile 的基本使用方法后 xff0c 我们再来了解一些在开发中使用 Dockerfile 的技巧 这一小节的展现方式与之前的略有不同 xff0c 其主要来自阅读收集和我自身在使用中的最佳实践 也许这里面
  • Docker学习笔记之通过 Dockerfile 创建镜像

    0x00 概述 由于 Docker 镜像的结构优势 xff0c 使它的占用空间远小于普通的虚拟机镜像 xff0c 而这就大幅减少了 Docker 镜像在网络或者其他介质中转移所花费的时间 xff0c 进而提高了我们进行迁移部署的效率 不过
  • K8S学习笔记之ETCD启动失败注意事项

    最近搭建K8S集群遇到ETCD的报错 xff0c 报错信息如下 xff0c 一定要关闭防火墙 iptables和SELINUX xff0c 三个都要关闭 xff01 xff01 Mar 26 20 39 24 k8s m1 etcd 643
  • 硬件笔记之Thinkpad T470P更换2K屏幕

    0x00 前言 手上的Thinkpad T470P屏幕是1920x1080的屏幕 xff0c 色域范围NTSC 45 xff0c 作为一块办公用屏是正常配置 xff0c 但是考虑到色彩显示和色域范围 xff0c 计划升级到2K屏幕 2k屏幕
  • Kafka学习笔记之Kafka High Availability(下)

    0x00 摘要 本文在上篇文章基础上 xff0c 更加深入讲解了Kafka的HA机制 xff0c 主要阐述了HA相关各种场景 xff0c 如Broker failover xff0c Controller failover xff0c To
  • Boyer-Moore算法的C++实现

    BM算法 阮一峰的网络日志 以上给出了通俗易懂的算法讲解 xff0c 下面给出代码实现 xff0c 使用的宽字符 xff0c 这样就不限于英文字母了 stdafx h编译不过去就屏蔽掉 StringSearch BoyerMoore cpp