zoukankan      html  css  js  c++  java
  • 7.KMP字符串

     

    s是长串,p是子串

    在数据范围较小时的暴力做法,两层循环

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int main() {
     4     int n, m;
     5     char s[100], p[100];
     6     cin >> m >> p + 1; //输入短串
     7     cin >> n >> s + 1; //输入长串
     8     for (int i = 1; i <= n; i++) {
     9         bool flag = true;
    10         for (int j = 1; j <= m; j++) {
    11             if (s[i + j - 1] != p[j]) {
    12                 flag = false;
    13                 break;
    14             }
    15         }
    16         if (flag) {
    17             cout << i << " ";
    18         }
    19     }    
    20     return 0;
    21 }

     KMP习惯下标从1开始

     

     KMP的核心就是在匹配失败时,p串往后最少移动到哪里继续尝试匹配

    然后要实现上述操作,KMP的关键点就是:

    寻找当前位置的前缀和后缀相等的长度的最大值。

    为什么是最大值呢。因为这相当于每次向后移动的距离是最小的。保证不会漏掉可能的匹配起始点

     也就是需要对p串的每一个点进行预处理

    上面黑字这句话就是next[]数组的含义

    next[i]表示以i为终点的后缀,和从1开始的前缀相等,而且这个后缀的长度最长

    如果next[i] = j的话

    绿颜色的长度是j

     next[i] = j也就是说对于p串

    p[1 ~ j] = p[i - j + 1, i]

    然后我们这个next数组在匹配的过程当中是如何运用的

     

     到i - 1,以及到j是匹配的

    此时如果s[i] != s[j + 1]的话

    然后就需要把红颜色的串,往后移动了

    然后就看从前往后最少移动多少

    这个最少移动多少就是直接调用next[j]

    最少移动的距离就是最大的后缀

    对于图中j这个点而言,也就是找到以j这个点为终点的后缀,和从1开始的前缀最长是多少

    以j为终点的这一段

     

     和从1开始的这一段相等

    对于j这个点,直接调用next[j]

    那么p串移动完之后的下标是多少,就是上图p串的绿色小串长度

    也就是next[j]

    也就是说p串需要移动到next[j]处

     然后当我们把p串换成next[j]处之后

    再看p串next[j]后的一个点是不是和s[i]匹配

    如果匹配继续往后做,如果不匹配,继续调用next数组,然后再看后面一个点是否和s[i]匹配

    举个样例来看p的next[]数组是啥

    前缀和后缀的长度一定要小于自己的长度

    ne[1] = 0 不能等于自己

    ne[i]就是从1~i这一段内的,前缀和后缀相等的最大长度

     

     时间复杂度O(n)

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 100010, M = 1000010;
     4 char p[N], s[M]; //p是子串,s是原串 
     5 int ne[N]; //next数组,针对子串 
     6 int main() {
     7     int n, m;
     8     cin >> n >> p + 1 >> m >> s + 1; //让串的下标从1开始 
     9     //求next数组[]的过程
    10     //1失败了就从0开始
    11     for (int i = 2, j = 0; i <= n; i++) { //是对p串预处理next数组
    12         while (j && p[i] != p[j + 1]) {
    13             j = ne[j];
    14         }
    15         if (p[i] == p[j + 1]) {
    16             j++;
    17         }
    18         ne[i] = j;
    19     } 
    20     //kmp的过程 
    21     for (int i = 1, j = 0; i <= m; i++) { //i是枚举s[i]
    22     //注意当前试图和s[i]匹配的是p[j + 1]
    23         while (j && s[i] != p[j + 1]) {
    24             //只要j没有退回起点
    25             j = ne[j];
    26         }
    27         if (s[i] == p[j + 1]) {
    28             j++;
    29         }
    30         if (j == n) { //匹配成功
    31             cout << i - n << " "; //全部匹配完了,然后减去子串长度,就是起始位置
    32             j = ne[j];
    33         }
    34     }
    35     return 0;
    36 }
  • 相关阅读:
    借书证信息管理系统,C语言实现
    以太坊 助记词提取 账户 公钥 私钥 最新实现可用。
    solc@0.6.3 web3@1.2.6 都是最新版本的,编译与部署示例
    ganache gas 错误
    MFC 记事本 文本编辑器
    课程设计 C语言 学生成绩管理系统
    cmake(.os .a)
    git recommend(alive)
    tensorflow tfrecoder read write
    300. 最长上升子序列
  • 原文地址:https://www.cnblogs.com/fx1998/p/13286129.html
Copyright © 2011-2022 走看看