1.前缀表(两个作用)

首先是理解前缀表,前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。记录下标i之前(包括i)的字符串中的最大相同前后缀长度。

2.前后缀概念

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。如 “aabaaf” 中,"aabaa"是最长前缀,"abaaf"是最长后缀。“a”的前后缀长度都为0。

3.前缀表计算方法

如字符串"aabaaf",

长度为前1个字符的子串a,最长相同前后缀的长度为0。

长度为前2个字符的子串aa,最长相同前后缀的长度为1。

长度为前3个字符的子串aab,最长相同前后缀的长度为0。

长度为前4个字符的子串aaba,最长相同前后缀的长度为1。

长度为前5个字符的子串aabaa,最长相同前后缀的长度为2。

长度为前6个字符的子串aabaaf,最长相同前后缀的长度为0。

那么把求得的最长相同前后缀的长度就是对应前缀表的元素,如图:

前缀长度转换成掩码 java 前缀长度有什么用_next数组

4.前缀表与next数组

next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(或者右移一位,初始位置为-1)之后作为next数组。
如上面的前缀表0 1 0 1 2 0,
统一减一为:-1 0 -1 0 1 -1,
右移一位为: -1 0 1 0 1 2。

5.代码构造next数组

构造next数组其实就是计算模式串s前缀表的过程,next[i] 表示 i(包括i)之前最长相等的前后缀长度(其实就是j)。 主要有如下四步:
1.初始化
2. 处理前后缀不相同的情况
3. 处理前后缀相同的情况
4. 更新next数组值

class Solution {
public:
    void getNext(int* next, const string& s) {
  	    //定义两个指针i和j,
  	    //j指向前缀终止位置(同时也是前缀长度),
    	//i指向后缀终止位置。因为j初始化为0,那么i就从1开始,进行s[i] 与 s[j]的比较。
        int j = 0;
        next[0] = 0;				//初始化,一个字符时前后缀长度都为0
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) {		//前后缀不同
                j = next[j - 1];		//向前回溯
            }
            if (s[i] == s[j]) {			//找到相同前后缀			
                j++;
            }
            next[i] = j;				
        }
    }
    //在文本串s里 找是否出现过模式串t。
    //定义两个下标j 指向模式串起始位置,i指向文本串起始位置.
    int strStr(string s, string t) {
        if (t.size() == 0) {
            return 0;
        }
        int next[t.size()];
        getNext(next, t);
        int j = 0;			//初始化
        for (int i = 0; i < s.size(); i++) {
            while(j > 0 && s[i] != t[j]) {		//不匹配
                j = next[j - 1];
            }
            if (s[i] == t[j]) {					//匹配
                j++;
            }
            if (j == t.size() ) {				//文本串s里出现了模式串t
                return (i - t.size() + 1);
            }
        }
        return -1;
    }
};