以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