串的匹配算法
BF算法
说明
-
BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
-
时间复杂度:O(mn)
示例
string s1, s2;
cin >> s1 >> s2;
int m = s1.size(), n = s2.size();
int i = 0, j = 0;
while (i < m && j < n)
{
if (s1[i] == s2[j])
i++, j++;
else
i = i - j + 1, j = 0;
}
cout << (j == n ? "YES" : "NO") << endl;
KMP
算法及其优化
原理和说明
- 在匹配过程中,主串的指针不需要回溯,只回溯模式串的指针(回溯到匹配失败位置前的模式串的内容的最长公共前后缀的长度),然后继续比较
- 优化后的
KMP
算法的优化点在于aaaaabaaaaac
与aaaaac
的匹配,b
与c
失配后与a
匹配(失配),然后根据next
数组,后面又是与a
匹配,这里是在做无用功
前缀:包含头不包含尾的所有子串
后缀:包含尾不包含头的所有子串
最长相等前后缀:两者的交集的中的最大长度
注意:若字符串长度为1,则没有前后缀
求next
数组(前缀表)
void get_next(string s, int m, int ne[])
{
ne[0] = -1;
if (m == 1)
return;
ne[1] = 0;
// i 表示要求 ne[i], cn 表示要与 s[i-1] 比对的字符的下标
int i = 2, cn = 0;
while (i < m)
{
while (cn > 0 && s[i - 1] != s[cn])
cn = ne[cn];
if (s[i - 1] == s[cn])
ne[i++] = ++cn;
else
ne[i++] = 0;
}
return;
}
匹配过程
int i = 0, j = 0;
while (i < m && j < n)
{
while (j > 0 && s1[i] != s2[j])
j = ne[j]; // 得到新的下标(>=0)
if (s1[i] == s2[j])
i++, j++;
else
i++;
}
cout << (j == s2.size() ? "YES\n" : "NO\n");
求nextval
数组
void get_nextval(string s, int m, int *ne, int *neval)
{
neval[0] = -1;
if (m == 1)
return;
int i = 0;
while (i < m)
{
if (s[i] == s[ne[i]])
neval[i] = neval[ne[i]]; // 跳过相同的匹配不上的字符
else
neval[i] = ne[i]; // 否则,与 next 一样
i++;
}
return;
}
优化后的匹配过程
int i = 0, j = 0;
while (i < m && j < n)
{
// 如果 j == -1,说明 i 这个位置不可能匹配,所以 i++, j = 0(即j++)
// 如果 s1[i] == s2[j], 说明 i 这个位置匹配上了,所以 i++, j++
if (j == -1 || s1[i] == s2[j])
i++, j++;
else // 其他情况: j != -1 且匹配不上,则 j = neval[j]
j = neval[j];
}
cout << (j == s2.size() ? "YES" : "NO") << endl;
究极完整代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
#define pii pair<int, int>
#define all(x) x.begin(), x.end()
const int MOD = 1e9 + 7;
const int N = 2e5 + 2;
void get_next(string s, int m, int *ne)
{
ne[0] = -1;
if (m == 1)
return;
ne[1] = 0;
int i = 2, cn = 0;
while (i < m)
{
while (cn > 0 && s[i - 1] != s[cn])
cn = ne[cn];
if (s[i - 1] == s[cn])
ne[i++] = ++cn;
else
ne[i++] = 0;
}
return;
}
void get_nextval(string s, int m, int *ne, int *neval)
{
neval[0] = -1;
if (m == 1)
return;
int i = 0;
while (i < m)
{
if (s[i] == s[ne[i]])
neval[i] = neval[ne[i]]; // 跳过相同的匹配不上的字符
else
neval[i] = ne[i]; // 否则,与 next 一样
i++;
}
return;
}
void kmp(string s1, string s2)
{
int m = s1.size(), n = s2.size();
int *ne = new int[n];
cout << "kmp匹配:\n";
get_next(s2, n, ne);
cout << "打印next数组:" << endl;
for (int i = 0; i < n; i++)
cout << ne[i] << ' ';
cout << endl;
int i = 0, j = 0;
while (i < m && j < n)
{
while (j > 0 && s1[i] != s2[j])
j = ne[j]; // 得到新的下标(>=0)
if (s1[i] == s2[j])
i++, j++;
else
i++;
}
cout << (j == s2.size() ? "YES\n" : "NO\n");
cout << endl;
}
void kmp_plus(string s1, string s2)
{
int m = s1.size(), n = s2.size();
int *ne = new int[n];
get_next(s2, n, ne);
int *neval = new int[n];
get_nextval(s2, s2.size(), ne, neval);
cout << "kmp_plus匹配\n";
cout << "打印nextval数组:" << endl;
for (int i = 0; i < n; i++)
cout << neval[i] << ' ';
cout << endl;
int i = 0, j = 0;
while (i < m && j < n)
{
// 如果 j == -1,说明 i 这个位置不可能匹配,所以 i++, j = 0(即j++)
// 如果 s1[i] == s2[j], 说明 i 这个位置匹配上了,所以 i++, j++
if (j == -1 || s1[i] == s2[j])
i++, j++;
else // 其他情况: j != -1 且匹配不上,则 j = neval[j]
j = neval[j];
}
cout << (j == s2.size() ? "YES" : "NO") << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
string s1, s2;
cin >> s1 >> s2;
kmp(s1, s2);
kmp_plus(s1, s2);
return 0;
}
// 输入
ababaaaaababaaab
ababaaab
// 输出
kmp匹配:
打印next数组:
-1 0 0 1 2 3 1 1
YES
kmp_plus匹配
打印nextval数组:
-1 0 -1 0 -1 3 1 0
YES