算法--以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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章