核心:主串不能回退,一直遍历
当子串没有重复的时候子串回退到头部
有重复的时候回退到最后一个重复位置
如果发生失配,从失配位置向前看,看字串是否存在两个真子串一个顶头一个顶尾
匹配到c的时候匹配失败,按照普通方式是子串回到起始位置,主串到上次开始匹配位置的下一个位置,也就是b,但是这样的话在匹配失败的位置c的前面等于发生"错位",
这样错位一直到
,这时候其实也是错位,但是刚好错位还跟第一次匹配错误位置前两个对应上了,由于并不知道第一次匹配错误的位置是否等于子串的下一个位置,所以需要比较一下b是否和a相等
这种过程其实浪费了许多时间,**假如失配位置前没有和子串头相等的,子串在失配位置前的移动都是明确知道结果的,那就是一直失配,**直到第一次失配位置才是未知的,所以要找一种方法让他略过确定失配的位置,直接到匹配结果未知的位置
看图可发现失配位置前和子串头有相等的地方可以直接比较失配位置和子串相等位置的下一个
所以要做一个表用来记录应该跳到子串的位置即前缀
如何求前缀
看字串是否存在两个真子串一个顶头一个顶尾
比如abcdab当最后一位发生失配,则下标应当跳转到下标1,即第二位,因为下标0已经匹配了,此时只需要第二位和主串当前比较即可,因为主串的前一位一定是a,不然也不会匹配到最后一位,每一位应当储存当前位之前和字符串从头往前看有几位匹配(不包括当前位,因为0下标的存在正好可以当当前位要比较的位置),注意,这里"当前位之前"并不是相向,而是相同方向看
vector<int> prefix(n_len + 1);
prefix[0] = -1;
int i = 0;
int j;
while (i < n_len)
{
j = prefix[i];
while (j != -1 && needle[j] != needle[i])
{
j = prefix[j];
}
//至此,needle[j]要么跟needle[i]相等,要么是-1,告诉下一位要比较的位是j+1
prefix[i + 1] = j + 1;
++i;
}
完整代码
class Solution {
public:
int strStr(string haystack, string needle) {
int n_len = needle.size();
int h_len = haystack.size();
vector<int> prefix(n_len + 1);
prefix[0] = -1;
int i = 0;
int j;
while (i < n_len)
{
j = prefix[i];
while (j != -1 && needle[j] != needle[i])
{
j = prefix[j];
}
//至此,needle[j]要么跟needle[i]相等,要么是-1,告诉下一位要比较的位是j+1
prefix[i + 1] = j + 1;
++i;
}
for (i = 0, j = 0; i < h_len; ++i)
{
while (j!=-1 && haystack[i] != needle[j])
{
j = prefix[j];
}
if (j == -1||haystack[i] == needle[j])
{
j += 1;
}
if (j == n_len) return i - j + 1;
}
return -1;
}
};