以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