使用kmp算法来寻找字符串 1.设计和改进next的数组是这个问题最重要的核心。初学者经常被公共真前缀和真后缀这两个概念搞得晕头转向。如果无法理解这两个概念,强烈建议去看看王道或者邓俊辉数据结构kmp算法的讲解,动笔写写画画可以快速掌握手动构造next数组的方法。但是,代码构建和算法理解又是两个概念,尤其还涉及到改进数组的概念 2.设计next数组的方法依然是我们的老方法,双指针法。我们用一个慢指针作为文本串指针,用一个快指针作为模式串的指针,慢指针从不回退,快指针一旦不匹配条件即快速回退至匹配处。还要掌握迭代的概念,在next数组的设计中,我们需要理解next[0]为什么被赋值成为-1,这实际上是采用一种哨兵结点的方式,虚拟模式串头部存在一个永久匹配的字符,这样及可以做到快速迭代,减少逻辑判断。而我们通过迭代的思想,在掌握next[i]时,如何求得next[i+1],区分两种情况,如果字符匹配,next[i+1]++,不匹配则快速回退至最近匹配处,t=next[i]重新匹配。 3.改进next数组的逻辑判断,对于000001的next数组的设计可以发现,next数组退化成暴力算法,这里就是因为没有吸收不匹配字符的教训情况,我们需要把这个教训利用起来。因此,next数组在构建的过程中,自然知道已构建的字符串信息,所以当出现相同的匹配字符时,应当及时避免再次使用相同字符再次进行匹配。
public:
int strStr(string haystack, string needle) {
int n=haystack.size();//记录文本串长度
int m=needle.size();//记录模式串长度
//排除意外情况
// 1. 修正边界条件:只有needle为空时返回0,其他无效情况返回-1
if (m == 0) return 0; // 模式串为空,按题目要求返回0
if (n < m) return -1; // 文本串比模式串短,不可能匹配
//构造next数组
int i=0;
int* next=new int[m];
int t=next[0]=-1;//注意,t不仅相当于慢指针,也是next数组中的值
//已知next[i]求next[i+1]
while(i<m-1){
if(t<0||needle[t]==needle[i]){
t++;
i++;
//改进next数组,通过对比是否为原来相同字符来吸取教训,例如00001情况,防止退化成暴力算法
next[i] = (needle[i] != needle[t]) ? t : next[t];
}
else
t=next[t];
}
//启用next表对文本串进行匹配
int j = 0; // 文本串指针
int p = 0; // 模式串指针(重新定义,避免和构造next的i冲突)
while (j < n && p < m) {
// 修正条件:p<0代表模式串指针回退到起点,直接匹配下一个字符
if (p < 0 || haystack[j] == needle[p]) {
j++;
p++;
} else {
// 不匹配时,模式串指针回退(利用next数组)
p = next[p];
}
}
// 释放动态数组
delete[] next;
// 4. 修正返回值:匹配成功返回起始索引,失败返回-1
if (p == m) {
return j - m;
} else {
return -1;
}
}
};