字符串匹配算法之KMP
2016-07-15 21:26
393 查看
【kmp算法思想】
说起字符串查找,大家肯定能理解朴素的查法,就是以 S 每个字符为开头与 W 比较。O(m*n)这种方法是基于回溯,但是这种回溯每次出现不匹配就要重头比较,没能很好的利用不匹配点之前已经进行的匹配比较,有很多回溯点可以直接判断是不可能匹配成功的,因此KMP变基于对子串的提前分析,在子串的位置j出现不匹配时,直接获得子串后移长度,减少了不必要的回溯,其效率可达到性O(m+n)。看下图,现在字符串查找过程中出现了不匹配:
按朴素算法,这里 W 应该右移一位然后重新匹配。但是,目前为止,蓝色色部分在两个字符串中都是已知的。我们就不用一个个比较了,直接滑动,跳过不匹配的就好:
以上就是 KMP 算法的思想,就是针对每个不匹配点,在子串开始位置到不匹配点位置,找到最长的滑动区间。
【数学表达】
针对字符串P,定义next特征函数如下:
其中k即为最长的相等前缀长度,所以当S[i]!=P[j]时,我们将子串P向后滑动j-k位,此时源串指针不动,子串指针指到p[next[j]]即p[k+1],接下来只需要继续比较S[i]和p[next[j]],因为根据定义,前面的字符肯定相同。
【next数组的求解】
next数组的求解可以使用递推的方式。
设已知next[j]=k+1,
则有p[0]p[1]...p[k]=p[j-1-k]p[j-k]...p[j-1],现在要求next[j+1],则:
(1)若p[0]p[1]...p[k]p[k+1]=p[j-1-k]p[j-k]...p[j-1]p[j] 即p[k+1]=p[j] 即 p[next[k]]=p[j],则next[j+1]=next[j]+1
(2)若p[next[j]]!=p[j],则next[j]肯定小于next[j]。根据KMP最长相等前缀、后缀的思路,我们对p[0]~p[k]再次找最长前缀,根据下图所示:
因为part1=part2=part3=part4,k=next[j],k=next[k],
若此时p[k]=p[j],则next[j+1]=next[next[j]]+1;
否则对part1继续上述的处理,直到k=-1.
【代码实现】
#include <string> #include <vector> #include <iostream> using namespace std; class KMP_Algorithm { public: KMP_Algorithm(){}; virtual ~KMP_Algorithm(){}; static int findSubstr(string& source, string& subStr); static void initNext(string& str, vector<int>& Next); static void KMP_Algorithm_test(); };方法实现:
#include "KMP_Algorithm.h" int KMP_Algorithm::findSubstr(string& source, string& subStr) { if (0 == source.length() || 0 == subStr.length()) return -1; if (source.length()<subStr.length()) { cerr << "[KMP_Algorithm] source string is shorter than subStr!" << endl; return -1; } vector<int> Next(subStr.length(), 0); KMP_Algorithm::initNext(subStr, Next); //Next[0]=-1; Next[j]=k+1, subStr[Next[j]] will compare with the last position£¬0<=k<j-1, p(0,k)=p(j-1-k,j-1);Next[j]=0,other int i = 0, pos = 0; int SLen = source.length(); int len = subStr.length(); while (i<SLen && pos<len) { if (pos == -1||source[i] == subStr[pos]) { i++; pos++; } else { pos = Next[pos]; } } if(pos == subStr.length()) { return i - subStr.length(); } else return -1; } void KMP_Algorithm::initNext(string& str, vector<int>& Next) { if (str.length() == 0) return; Next[0] = -1; int j = 0, k = -1, Len = str.length(); while (j<Len) { if (k == -1 || str[j] == str[k]) { j++; k++; if (j<Len) Next[j] = k; } else k = Next[k]; } return; } void KMP_Algorithm::KMP_Algorithm_test() { string s1; string s2; while (1) { cout << "[Enter source string]:"; getline(cin, s1); cout << "[Enter sub string ]:"; getline(cin, s2); cout << "[pos]" << KMP_Algorithm::findSubstr(s1, s2) << endl;; } }【测试】
#include "KMP_Algorithm.h" void main() { KMP_Algorithm::KMP_Algorithm_test(); int a; cin >> a; }
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- C#递归算法之分而治之策略
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- C#算法之大牛生小牛的问题高效解决方法
- cmd findstr 字符串查找增强使用说明
- C#算法函数:获取一个字符串中的最大长度的数字
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- 经典排序算法之冒泡排序(Bubble sort)代码
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法