主要作用:能够在线性复杂度内求出一个串在另一个串的所有匹配位置。
说明:设模板串是 pattern, 令 next[i] = max{k|[pattern[0..k−1] = pattern[i−k+l..i]
得到。求解next[i]可以使用动态规划,即 next[i+1] 可以由next[i],next[next[i]]
,…得到。
得到next[i]们数组之后,设两个指针i和j,分别指向文本串和模式串,成功匹配得向后移动j,否则把/移动到 next[j]。 当j移动到模式串末尾时,就说明匹配成功。
首先是next[i]数组
vector<int> get_next(string ss)
{
//next[i] ,下标为i的 字符前 最长相等前后缀的长度
int n = ss.size();
vector<int>next(n + 1, 0);
for (int i = 1; i < n; ++i)
{
int j = i;
while (j > 0)
{
// 动态规划
j = next[j];
if (ss[j] == ss[i])
{
// i+1 是因为当前的前后缀相等的是其后边的结果
next[i + 1] = j + 1;
break;
}
}
}
return next;
}
KMP 匹配
vector<int>position;
int m = text.size();
for (int i = 0, j = 0; i < m; ++i)
{
//cout << "j :" << j << endl;
if (j < n && text[i] == pattern[j])
++j;
else
{
while (j > 0) //因为没有如果没有,next[j] 的值是0
{
// 回溯到最优位置,如果不为0,那么该位置前边 next[j] -1的元
// 素与i位置前边next[j] -1元素是一样的
j = next[j];
if (text[i] == pattern[j])//作用是如果回溯到的地方匹配上了,就从这个地方开始匹配
{
j++;//因为循环中有 i++
break;
}
}
}
if (j == n)
position.push_back(i - n + 1);
}
综合:
vector<int> find_substring(string pattern, string text)
{
//next[i] ,下标为i的 字符前 最长相等前后缀的长度
int n = pattern.size();
vector<int>next(n + 1, 0);
for (int i = 1; i < n; ++i)
{
int j = i;
while (j > 0)
{
// 动态规划
j = next[j];
if (pattern[j] == pattern[i])
{
// i+1 是因为当前的前后缀相等的是其后边的结果
next[i + 1] = j + 1;
break;
}
}
}
vector<int>position;
int m = text.size();
for (int i = 0, j = 0; i < m; ++i)
{
if (j < n && text[i] == pattern[j])
++j;
else
{
while (j > 0)
{
// 回溯到最优位置,如果不为0,那么该位置前边 next[j] -1的元素与i位置前边next[j] -1元素是一样的
j = next[j];
if (text[i] == pattern[j])
{
j++;
break;
}
}
}
if (j == n)
position.push_back(i - n + 1);
}
return next;
}
详细解释可以看这篇文章:
KMP算法详解