1.朴素的模式匹配算法
Index(S,T,pos):将主串S的第pos个字符和模式串的第一个字符比较(pos是位置,在算法中要转化为索引),若相等,继续逐个比较后续字符。如不等,从主串的下一个字符起,重新与模式串的第一个字符比较。直到匹配成功或全部遍历完成为止。
这里需要说明一个前提:在一些教科书中,序号默认是从1开始的(序号为0的地方储存串的长度),但是在C/C++语言中,字符串都是从序号为0的地方开始的,因此对代码做出了一些更改。
要匹配的串称为模式串,与之比对的称为主串。
int Index(string s, string t, int pos) {
int i = pos-1, j = 0;
while (i < s.length() && j < t.length()) {
if (s[i] == t[j])
{
++i;
++j;
}
else
{
i = i - j + 1;
j = 0;
}
}
if (j >= t.length())
return i - t.length()+1;//返回的是位置,索引=位置-1
else
return 0;
}
(1)初始化
int i = pos-1, j = 0;
pos是主串开始检索的位置,对应i的索引为pos-1。要匹配的串则是从序号为0的地方开始。
(2)核心代码
while (i < s.length() && j < t.length()) {
if (s[i] == t[j])
{
++i;
++j;
}
else
{
i = i - j + 1;
j = 0;
}
}
while退出的条件:主串遍历完毕或者配对成功。i<s.length()是主串还没有遍历完毕的时候,当i>=s.length()的时候,就说明主串已经遍历完毕了。而如果j>=t.length()的话,说明配对成功。
如果s[i]==t[j],说明对应部分比对上了,那么i和j都需要移动一个位置。
如果s[i]!=t[j],说明比对失败,j要回到0(要匹配的串从头开始比对),而i也要移动到起始位置,只不过起始位置需要变化(上一次的起始位置+1),也就是 i=i-j+1。
(3)返回值
if (j >= t.length())
return i - t.length()+1;//返回的是位置,索引=位置-1
else
return 0;
如果j>=t.length(),说明匹配成功,这样需要返回主串对应的序号(也就是从哪里开始比对上的),i-t.length()是起始的索引,位置=索引+1。在这里返回位置(因为索引也有可能是0)。
如果while循环结束,j还是小于t.length(),则while循环是因为主串循环完毕退出的,说明没有匹配上,这时返回0表示匹配失败。
示例:
int main() {
string s,t;
cout << "输入主串:";
cin >> s;
cout << "输入要匹配的串:";
cin >> t;
int h = Index(s, t, 1);
cout << h;
return 0;
}
2.KMP模式匹配算法
https://www.cnblogs.com/dusf/p/kmp.html
这个是大神的详细解释,在这里就不重复了,只贴上代码。
(1)求next数组
void Getnext(string t, int next[])
{
int j = 0, k = -1;
next[0] = -1;
while (j < t.length() - 1)
{
if (k == -1||t[j]==t[k])
{
j++;
k++;
next[j] = k;
}
else
k = next[k];
}
}
(2)KMP算法
int KMP(string s, string t)
{
int next[MaxSize], i = 0,j = 0;
Getnext(t, next);
while (i < (int)s.length() && j < (int)t.length())
{
if (j == -1 || s[i] == t[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j >= t.length())
return i - t.length(); //匹配成功,返回子串的索引
else
return -1; //没找到
}
这里需要解释一个问题:为什么s.length()和t.length()需要强制转换为int类型?
while (i < (int)s.length() && j < (int)t.length())
因为s.length()和t.length()返回的字符串长度是unsigned int类型,如果j==-1的话,负数与unsigned int类型比较会转化为unsigned int类型——最终结果为4294967295。
因此如果不强制转化为int类型,当j==-1的时候while循环就退出了。
注:这里的返回值是模式串在主串里的索引,如果想返回位置则需要+1。
(3)KMP算法的改进
其实也就是对Getnext函数的改进。
void Getnext(string t, int* next)
{
int j = 0, k = -1;
next[0] = -1;
while (j < (int)t.length() - 1)
{
if (k == -1 || t[j] == t[k])
{
j++; k++;
if (t[j] == t[k])//当两个字符相同时,就跳过
next[j] = next[k];
else
next[j] = k;
}
else k = next[k];
}
}
示例:
int main() {
string s;
string t;
cout << "输入主串:";
cin >> s;
cout << "输入模式串:";
cin >> t;
int g= KMP(s, t);
printf("%d", KMP(s, t));
return 0;
}