CHAPTER_12 提高篇(6)——字符串专题
12.2.1 字符串匹配问题
本节主要讨论字符串匹配的问题,如果给出两个字符串text和pattern,需要判断pattern是否为text的子串。一般把text称作文本串,把pattern称作模式串。
字符串匹配的暴力解法很容易想到,只需要枚举文本串的起始位置 i ,然后从该位开始逐位与模式串进行匹配,如果匹配过程中每一位都相同,则匹配成功;否则,只要出现某位不同,就让文本串的起始位置变为 i+1,并从头开始模式串的匹配。这种做法时间复杂度位O(mn),其中m和n为模式串和文本串的长度。对于一些长字符串,这个时间复杂度让人无法接受。
我们有更优秀的KMP算法来解决字符串匹配问题,时间复杂度为O(m+n)。
12.2.2 next数组
在正式进入KMP算法之前,先要学习一个重要数组。假设有一个字符串s(下标从0开始),那么它以 i 号位作为结尾的子串就是s[0...i]。对该子串来说,长度为k+1的前缀和后缀分别是s[0...k]与s[i-k...i]。现在定义一个int型数组next,其中next[i]表示使子串s[0...i]的前缀s[0...k]的前缀等于后缀s[i-k...i]的最大的k(注意前缀和后缀可以部分重叠,但不能是s[0...i]本身);如果找不到相等的前后缀,那么就令next[i]=-1。显然,next[i]就是所求最长相等前后缀中前缀最后一位的下标。
以字符串"ababaab"作为举例,next数组的计算过程如下图:
那么。如何求解next数组呢?我们采用递推的方法求解,即假设已经求出了next[0]~next[i-1],现在要求next[i]。
以"abababc"为例子,《算法笔记》中将next数组的求解过程清晰地阐述如下:
看懂了next数组的求解过程,我们总结出逻辑:
(1)初始化next数组,令j=next[0]=-1。
(2)让 i 在1~len-1的范围内遍历,对每个 i ,执行(3)(4)。
(3)不断令j=next[j],直到 j 回退至-1,或是s[i]==s[j+1]成立。
(4)如果s[i]==s[j+1],next[i]=j+1;否则next[i]=j。
//求解长度为len的字符串s的next数组
void getNext(char s[],int len) {
int j=-1;
next[0]=-1;
for(int i=1;i<len;i++) {
while(j!=-1&&s[i]!=s[j+1]) {
j=next[j];
}
if(s[i]==s[j+1]) {
j++;
}
next[i]=j;
}
}