字符串相关算法:KMP、

KMP算法

next数组构建

定义: 对于字符串 p p p n e x t [ i ] next[i] next[i]表示 p p p [ 0 , i ] [0, i] [0,i]上的子串的最长相同前后缀长度

暴力求解: 很明显,可以在 [ 0 , i ) [0, i) [0,i)上枚举长度来构建 n e x t next next,此时算法复杂度为 O ( m 2 ) O(m^2) O(m2) m m m p p p的长度

优化求解: 见到最长两字容易想到使用递推也就是动态规划去求解,此时状态就是next数组的定义;求解 n e x t [ i ] next[i] next[i]时已知字串 [ 0 , i − 1 ] [0, i-1] [0,i1]的最长相同前后缀长度 n e x t [ i − 1 ] = j next[i-1]=j next[i1]=j,也就是说该子串对应前缀为子串 [ 0 , j ) [0, j) [0,j);此时出现两种情况:

(1) p [ j ] = p [ i ] p[j]=p[i] p[j]=p[i],有 n e x t [ i ] = n e x t [ i − 1 ] + 1 ; next[i]=next[i-1]+1; next[i]=next[i1]+1;
在这里插入图片描述
(2) p [ j ] ≠ p [ i ] p[j] \neq p[i] p[j]=p[i],此时需要不断缩短子串 [ 0 , j ) [0, j) [0,j),找到子串 [ 0 , i − 1 ] [0, i-1] [0,i1]上的次长相同前后缀长度,但我们只知道最长相同前后缀长度 n e x t [ i − 1 ] next[i-1] next[i1];但不难由 n e x t [ i − 1 ] = j next[i-1]=j next[i1]=j得子串 [ 0 , j ) [0, j) [0,j)等于子串 [ i − j , i − 1 ] [i-j, i-1] [ij,i1],而子串 [ 0 , i − 1 ] [0, i-1] [0,i1]的次长相同前后缀的前缀必定落在子串 [ 0 , j ) [0, j) [0,j)上、后缀必定落在子串 [ i − j , i − 1 ] [i-j, i-1] [ij,i1]上,由于这两个串相等,求解子串 [ 0 , i − 1 ] [0, i-1] [0,i1]次长相同前后缀问题可转化为求解子串 [ 0 , j ) [0, j) [0,j)的最长相同前后缀,也就是 n e x t [ j − 1 ] next[j-1] next[j1];不断递归 j = n e x t [ j − 1 ] j=next[j-1] j=next[j1]直至 p [ j ] = = p [ i ] p[j]==p[i] p[j]==p[i] j = = 0 j==0 j==0,要么没找到相同字符,此时 n e x t [ i ] = 0 next[i]=0 next[i]=0,要么找到了,此时 n e x t [ i ] = j + 1 next[i]=j+1 next[i]=j+1
在这里插入图片描述
此时根据均摊分析可知复杂度为 O ( m ) O(m) O(m)

字符串匹配

首先想到的还是暴力求解: 每次匹配失败时指针 i i i 在主串 S S S 上回到初始位置并向后移动一格,指针 j j j 在模式串 P P P 上回到初始位置
在这里插入图片描述
由上图可知这种移动方式是非常耗时的,因为字符 c c c 在前面几个字符 a b ab ab 中并没有出现,完全可以直接将模式串 P P P 相对于主串 S S S 移动到指针 i i i 指向的字符 a a a 处。
在这里插入图片描述

优化求解: 对于指针 j j j n e x t [ j − 1 ] next[j-1] next[j1] 表示子串 [ 0 , j − 1 ] [0, j-1] [0,j1] 的最长相同前后缀;
 
如果 n e x t [ j − 1 ] = 0 next[j-1]=0 next[j1]=0,意味着字符 P [ j − 1 ] P[j-1] P[j1] 不会出现在 P [ 0 , j − 2 ] P[0, j-2] P[0,j2],而且因为 P P P S S S 已部分匹配,有 S [ i − j , i − 1 ] = P [ 0 , j − 1 ] S[i-j, i-1]=P[0, j-1] S[ij,i1]=P[0,j1] ,因此有 ∀ k ∈ [ 0 , j − 2 ] , P [ k ] ≠ S [ i − 1 ] \forall k \in [0, j-2], P[k] \neq S[i-1] k[0,j2],P[k]=S[i1],即模式串P相对于主串 S S S 移动 [ 1 , j − 2 ] [1, j-2] [1,j2] 位,都不会把 S [ i − 1 ] S[i-1] S[i1] 匹配上,这时候就可以把 S [ i − 1 ] S[i-1] S[i1] 放弃掉,此时对应的就是上面那种情况。
 
对于一般的 n e x t [ j − 1 ] ≠ 0 next[j-1] \neq 0 next[j1]=0 ,如下图,此时 n e x t [ j − 1 ] = 2 next[j-1]=2 next[j1]=2 ,同样的也有 S [ i − j , i − 1 ] = P [ 0 , j − 1 ] S[i-j, i-1]=P[0, j-1] S[ij,i1]=P[0,j1] ,因此有 P [ j − 2 , j − 1 ] = S [ i − 2 , i − 1 ] = P [ 0 , 1 ] P[j-2, j-1] = S[i-2, i-1] = P[0, 1] P[j2,j1]=S[i2,i1]=P[0,1] ,也就是说 P [ 0 ] P[0] P[0] P [ 1 ] P[1] P[1] 是能够匹配上 S [ i − 2 ] S[i-2] S[i2] S [ i − 1 ] S[i-1] S[i1] 的,可以将 P P P 相对于 S S S 移动到该位置继续匹配 S [ i ] S[i] S[i] P [ j ] P[j] P[j] ,如下图①和②;
在这里插入图片描述
但此时 S [ i ] S[i] S[i] P [ j ] P[j] P[j]依旧没有匹配上,且有 n e x t [ j − 1 ] = 1 next[j-1] = 1 next[j1]=1 ,继续移动,如上图②和③, S [ i ] S[i] S[i] P [ j ] P[j] P[j] 终于匹配上了,且随着 i i i j j j 同时继续往下走,模式串 P P P 终于匹配上主串 S S S
这个过程中存在一个递归关系 j = n e x t [ j − 1 ] j=next[j-1] j=next[j1],且递归终止条件为 j = = 0 ∣ ∣ S [ i ] = = P [ j ] j==0 || S[i] == P[j] j==0S[i]==P[j]这里的递归公式与动态求解 n e x t next next 数组一致,这个过程中 j j j 越来越小也就是说相对于初始位置、 P P P 相对于 S S S 移动的量越来越大,能够排除暴力求解中的一些情况的同时不漏掉所有可能匹配上的情况

CPP代码

int strStr(string haystack, string needle) {
        int m = needle.size(), n = haystack.size();
        if (m == 0) return 0;
        if (n < m) return -1; 
        vector<int> next(m);
        for (int i = 1, j = 0; i < m; ++i) {
            while (j > 0 && needle[i] != needle[j]) j = next[j-1];
            if (needle[i] == needle[j]) ++j;
            next[i] = j;
        }
        for(int i = 0, j = 0; i < n; ++i) {
            while (j>0 && haystack[i] != needle[j]) j = next[j-1];
            if (haystack[i] == needle[j]) {
                ++j;
                if (j == m) return i-m+1;
            }
        }
        return -1;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值