算法导论--以KMP实现的字符串匹配

以KMP实现的字符串匹配

性质

给定模式集合,和源集合。
算法输出源集合中和模式匹配的所有相关处的起始位置。

接口设计

template<typename T>
class CharacterMatch
{
public:
	CharacterMatch();
	~CharacterMatch();
public:
	DataStruct::Array::DynArray<int> RunKMP(
		const DataStruct::Array::DynArray<T>& arrPattern_,
		const DataStruct::Array::DynArray<T>& arrSource_);
private:
	DataStruct::Array::DynArray<int> PatternPreAnalysis(
		const DataStruct::Array::DynArray<T>& arrPattern_);
};

实现

构造

template<typename T>
CharacterMatch<T>::CharacterMatch()
{

}

析构

template<typename T>
CharacterMatch<T>::~CharacterMatch()
{

}

算法运行

template<typename T>
DataStruct::Array::DynArray<int> CharacterMatch<T>::RunKMP(
	const DataStruct::Array::DynArray<T>& arrPattern_,
	const DataStruct::Array::DynArray<T>& arrSource_)
{
	DataStruct::Array::DynArray<int> _arrRet;
	DataStruct::Array::DynArray<int> _nPatternRet = PatternPreAnalysis(arrPattern_);
	int _nPreMatchLen = 0;
	int _nCurMatchLen = 0;
	for (int _i = 0; _i < arrSource_.GetSize(); _i++)
	{
		if (_nPreMatchLen == arrPattern_.GetSize())
		{
			_nPreMatchLen = _nPatternRet[_nPreMatchLen - 1];
		}

		if (arrSource_[_i] == arrPattern_[_nPreMatchLen])
		{
			_nCurMatchLen = _nPreMatchLen + 1;
		}
		else
		{
			_nCurMatchLen = 0;
			while (_nPreMatchLen > 0)
			{
				_nPreMatchLen = _nPatternRet[_nPreMatchLen - 1];
				if (arrSource_[_i] == arrPattern_[_nPreMatchLen])
				{
					_nCurMatchLen = _nPreMatchLen + 1;
					break;
				}
			}
		}

		_nPreMatchLen = _nCurMatchLen;
		if (_nCurMatchLen == arrPattern_.GetSize())
		{
			_arrRet.Add(_i - arrPattern_.GetSize() + 1);
		}
	}

	return _arrRet;
}

template<typename T>
DataStruct::Array::DynArray<int> CharacterMatch<T>::PatternPreAnalysis(
	const DataStruct::Array::DynArray<T>& arrPattern_)
{
	DataStruct::Array::DynArray<int> _nPatternRet;
	int _nSize = arrPattern_.GetSize();
	for (int _i = 0; _i < _nSize; _i++)
	{
		int _nNum = 0;
		int _nMayNum = _i;
		for (; _nMayNum > 0; _nMayNum--)
		{
			int _nStartIndex = _i - _nMayNum + 1;
			bool _bMatch = true;
			for (int _k = 0; _k < _nMayNum; _k++)
			{
				if (arrPattern_[_nStartIndex + _k] != arrPattern_[_k])
				{
					_bMatch = false;
					break;
				}
			}

			if (_bMatch)
			{
				_nNum = _nMayNum;
				break;
			}
		}

		_nPatternRet.Add(_nNum);
	}

	return _nPatternRet;
}

算法性质&正确性证明

对输入序列每个元素进行遍历
保持记录,到上一元素位置 为止可以与 模式达成的最大匹配的长度PreMatchLen

如果PreMatchLen > 0 且 本位置元素 和 模式PreMatchLen位置元素 不匹配
易于知道,本位置为止 与模式达成的最大匹配长度
只能在 0,...,PreMathcLen中
令k=PreMatchLen-1模式序列中前PreMatchLen个元素设为 x0 x1 ... xk

当前位置i为止可与模式达成最大匹配长度 设为 len
len <= PreMatchLen
1. 从当前位置往前len-1个位置开始 到 当前位置前一位置的 len-1个输入序列元素
是模式序列中前PreMatchLen个元素设为 x0 x1 ... xk 的一个长度为len-1的前缀

2. 从当前位置往前len-1个位置开始 到 当前位置前一位置的 len-1个输入序列元素
也是模式序列中前PreMatchLen个元素设为 x0 x1 ... xk 的一个长度为len-1的后缀

首先,对于当前位置前一元素p,
我们知道 该元素p和 xk是匹配的
从p次往前的p'
从xk依次往前x'
只要往前元素个数在PreMaxLen范围
则有p' 和 x'是匹配的

len-1<= PreMaxLen-1在此范围内
故有
2的结论成立。

综合,此位置的与模式产生长度为len的最大匹配中
满足
len的前len-1个序列元素是 模式前PreMaxLen个元素序列的 后缀和前缀

经过预先处理
我们可以找到满足
模式前PreMaxLen个元素序列 即是该序列前缀又是该序列后缀的最长序列。
对该序列进行验证,
验证指的是 
假设满足要求的序列长度为t
验证即为,验证 当前位置 和 模式第t+1元素位置 是否匹配,若匹配,则验证通过。更新CurMaxLen。
验证失败,
我们需要寻找满足
对模式前PreMaxLen个元素序列 即是该序列前缀又是该序列后缀的其他可能序列。
假设 满足该要求的最长序列,经过验证不通过。
对于其他满足要求的序列
假设最长序列为 x1, ..., xa
不妨设一个满足要求的非最长序列为
x1,...,xb
b < a
模式前PreMaxLen个元素序列
x1,....,xk
x1,...,xb是x1,...,xa的前缀
x1,...,xa是x1,....,xk后缀
x1,...,xb也是x1,...,xk后缀
b < a
x1,...,xb也是x1,...,xa后缀
综合
x1,...,xb
满足既是x1,...,xa前缀又是x1,...,xa后缀
故对x1,...,xa序列验证失败后,取x1,...,xa序列最长的前缀后缀进行后续验证
并可如此迭代

如果迭代到某次,找不到序列x1,...,xz的最长前缀和后缀,则依然验证
若验证通过,PreMaxLen更新为1
若验证不通过,PreMaxLen更新为0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值