算法需求
主串S长度n,模式串P长度m,匹配情况是S串的i位置,P串的j位置失配,这个时候为了防止匹配的指针回溯和一些不必要的匹配次数,所以需要查找在S串中“失配”的位置i之前匹配到了P串中j位置k之前的串,这样就可以从k位置开始匹配了,省去了从新匹配的效果
补充:
- 其最终原理是采用了 在模式串中查找 前缀和后缀匹配的最长长度,用空间换时间的问题
- 则用【反正法】: 如果s[i] !=p[j], 则下一个开始的位置为在x~i之间移动, 那么如果接下来跟p这个模式子串匹配了,那么因为p向右移动导致了,其实跟在i之前的跟s匹配的部分反而少了,所以 如果因为移动了,并且匹配上了, 说明 s中 [x,i] 之间 肯定有一部分是和p匹配的,这个就是前缀和后缀的匹配位置,也就是说如果在【x,1】之间如果匹配的到了p,那么一定有有重合的后半部分和前半部分相匹配,如果没有那么直接跳转到p[1] 和s[i]直接开始匹配就行了,如果怕漏了,即略过了匹配的地方,那么刚刚反正的,如果有匹配的,那么一定存在前后缀的匹配,那么从前后缀匹配开始就行了,而且需要选在最大的那个后缀
算法推理
-
假设此时应与模式中第k(k< j)个位置
-
模式中前k-1个字符的字串必须满足下列关系,并且不存在k`>k这种情况
P 1 . . . . . P k − 1 = S i − k + 1 . . . . . S i − 1 P_ {1}.....P_ {k-1} =S_{i-k+1}.....S_{i-1} P1.....Pk−1=Si−k+1.....Si−1 -
且已经得到的匹配结果为
P j − k + 1 . . . . . P j − 1 = S i − k + 1 . . . . . S i − 1 P_ {j-k+1}.....P_ {j-1} =S_{i-k+1}.....S_{i-1} Pj−k+1.....Pj−1=Si−k+1.....Si−1 -
综合上述的结果可以得到
P 1 . . . . . P k − 1 = P j − k + 1 . . . . . P j − 1 P_ {1}.....P_ {k-1} =P_ {j-k+1}.....P_ {j-1} P1.....Pk−1=Pj−k+1.....Pj−1 -
这个时候求得k值即可知道移动的位置
疑问
可能会疑问,确定移动出去的j-k的哪几个元素没有匹配的吗?
eg: 用上面的反正就可以知,我们求的就是最大的前缀匹配
K值求解
模式串的的next[j]值,取决于模式串的本身而和相匹配的主串无关,可以从分析其定义出发用递推的方式求的next的函数值
-
next[1] =0
-
设next[j]=k,则 P 1 ⋅ ⋅ ⋅ P k − 1 = P j − k + 1 ⋅ ⋅ ⋅ P j − 1 P_{1}···P_{k-1} = P_{j-k+1}···P_{j-1} P1⋅⋅⋅Pk−1=Pj−k+1⋅⋅⋅Pj−1,其中1< k < j,且不存在k` > k
-
那么next[j+1]就得视情况而定
a. 当 P k = P j P_{k} = P_{j} Pk=Pj时P 1 ⋅ ⋅ ⋅ P k = P j − k + 1 ⋅ ⋅ ⋅ P j P_{1}···P_{k} = P_{j-k+1}···P_{j} P1⋅⋅⋅Pk=Pj−k+1⋅⋅⋅Pj,则next[j+1] = k`
b. 当 P k ! = P j P_{k} != P_{j} Pk!=Pj时
这是就相当于 P 1 ⋅ ⋅ ⋅ P j P_{1}···P_{j} P1⋅⋅⋅Pj为目标字符串 P 1 ⋅ ⋅ ⋅ P k P_{1}···P_{k} P1⋅⋅⋅Pk为模式字符串,这个时候问题就相当于重新求 P 1 ⋅ ⋅ ⋅ P k P_{1}···P_{k} P1⋅⋅⋅Pk字符串的next[k],并比较next[k] == p[j],若果相等则next[j+1] = next[k]+1;
逻辑问题
- 原来是 主串中 x~i 中的前后匹配的串,但是其实是求的 模式串中0-j中匹配的前后串
- 因为转化为了 模式串中的各个位置的最佳匹配的串,而且前提是next[1] = 0; 题设是在 next[j] = k 且不存在k`>k的情况下的题设,且next[j+1] 依赖的是next[j]之前的已经匹配的串的情况的,所以 地推的都是最大情况