虽然今年寒假时已经结合王道机试指南过了一遍KMP算法,然鹅。。笔者今天又花了大半天才弄清楚KMP算法的工作原理。为了避免重复今天的失态,在本文中整理了KMP的内容。
1. 基本原理
首先介绍什么是模式匹配?
- 存储结构——顺序结构
- 什么是模式匹配?给定两个串S=”s1s2…sn”和T=”t1t2…tm”,在主串S中寻找子串T的过程称为模式匹配。
- T称为模式串,如果匹配成功,返回T在S中的位置,如果匹配失败,返回0.
因为笔者的文笔一般,理解的也不算多透彻,在这里放一篇大佬整理的关于朴素的模式匹配算法和KMP算法的教程帖:
https://blog.csdn.net/daijin888888/article/details/70212563
文中令我印象最深刻的是这句:(即T前部=T后部,T后部=S后部,则T前部=S后部)
细品之后才发现,这句话道出了KMP算法的核心原理,在搜索时进行跳转的原因和具体跳转到什么地方,都和这句话有关。
详细的内容,请读者自己翻看上文或者找别的经验贴吧。下面开始编程:
2. 朴素的模式匹配算法
人称“暴力算法”,耗时耗内存。讲道理,笔者也不知道在机试的时候能不能用这个。如果测试用例比较多的话,用这个方法应该可以拿到较小的测试用例的分数吧。虽然是杀敌一千,自损八百的操作。。。
#include<iostream>
#include<cstdio>
using namespace std;
int BF(char S[], char T[])
{
int i = 0, j = 0;
while (S[i] != '\0' && T[j] != '\0')
{
if (S[i] == T[j]) { i++; j++; } // 继比较后继字符
else { i = i - j + 1; j = 0; } // 指针后退重新开始匹配
}
if (T[j] == '\0') return i - j + 1;
else return 0;
}
int main()
{
char a[] = "ldncdncdnckmsajnabjnksn";
char b[] = "nck";
int position = BF(a, b);
cout << position << endl;
return 0;
}
3. KMP算法
本文核心,但是能说的是不是说完了呢???
建议在阅读代码的时候,自己在VS上逐步调试或者在纸上写写匹配的过程,一定要和理论的算法对应起来。
有的代码是搜索数字,用int 数组存储;有的代码是搜索字符串,用string存储。在本文中,考虑的是字符串,代码如下:
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
const int MAXN = 1000000;
int nextTable[MAXN];
void GetNextTable(string pattern)
{
int m = pattern.size();
int i = 0;
int j = -1;
nextTable[i] = j;
while (i < m)
{
if (j == -1 || pattern[i] == pattern[j])
{
i++;
j++;
nextTable[i] = j;
}
else
{
j = nextTable[j];
}
}
//for (int i = 0; i < m; i++)
//{
// cout << pattern[i] << " : " << nextTable[i] << endl;
//}
return;
}
int KMP(string text, string pattern)
{
GetNextTable(pattern);
int n = text.size();
int m = pattern.size();
int i = 0;
int j = 0;
int number = 0;
while (i < n)
{
if (j == -1 || text[i] == pattern[j])
{
i++;
j++;
}
else
{
j = nextTable[j];
}
if (j == m)
{
number++;
j = nextTable[j];
}
}
return number;
}
int main()
{
int caseNumber = 0;
string text, pattern;
cin >> caseNumber;
while (caseNumber--)
{
while (cin >> pattern >> text )
{
printf("%d\n", KMP(text, pattern));
}
}
return 0;
}
4.总结
虽然花费了一整天来理解这个算法,我觉得还是挺值的。如果脑子不够灵光,就用努力和时间去弥补。不管是眼下的保研,还是未来的工作,皆是如此。