zoukankan      html  css  js  c++  java
  • kmp学习笔记(模板)

    介绍

    Knuth-Morris-Pratt字符串查找算法(简称为KMP算法
    用于字符串的模式匹配。相比朴素算法,它的时间复杂度为O(n + m),其中n是文本串长度,m是模式串长度。

    原理

    kmp通过计算模式串的next数组作为辅助来减少朴素算法中重复的比对。

    next数组代表模式串的最长相同真前后缀。例如:在模式串s =“ABABCD”中,它的next数组为next=[-1 0 0 1 2 0 0]。其中next[0]为-1为的是保证完全失配时不陷入死循环。

    假设有文本串t="ABADDABABD"。开始以位置0为起点,在匹配到t[0:2]="ABA"都相等;但是匹配到t[3] = 'D' != s[3] = 'C',这时不需要从位置1重新开始。因为由next[3]=1知道,s串位置3之前最长相同真前后缀为1,即s[0:0] == s[2:2]。因此可以直接从文本串的位置3开始和模式串的位置1匹配,即next[3]位置。

    如何构造next数组?假设包括位置 i 前的next数组已经构建完成,设j = next[i]。如果s[i + 1] == s[j + 1],那么显然next[i + 1] = j + 1;否则,由于s[0 : next[j]]存在真前缀和s[0 : i - 1]真后缀相等,故令j = next[j],如果s[i + 1] == s[j + 1],那么next[i + 1] = j + 1,否则重复上述过程,直到j = next[0] = -1代表不存在相同的真前后缀,那么next[i + 1] = 0。

    时间复杂度O(n)

    实现

    板子都很好写

    //求next
    int nt[N]; //next
    void getnext(char t[]) {
        int i = 0, j = -1;
        nt[i] = j;
        while(t[i]) {
            if(j == -1 || t[i]  == t[j]) {
                i++;
                j++;
                nt[i] = j;
            } else {
                j = nt[j];
            }
        }
    }
    
    void search(char s[], char t[]) { //s文本串,t模式串
        int i = 0;
        int j = 0;
        while(s[i]) {
            if(j == -1 || s[i] == t[j]) {
                i++;
                j++;
                if(!t[j]) { //找到了
                    //处理
                    j = nt[j]; //继续找。如果只找一次可以直接break
                }
            } else {
                j = nt[j];
            }
        }
    }
    

    应用

    除了匹配模式串,还可以找循环节,找最长相同前后缀等等。具体看题。

    扩展kmp

    设文本串为s(长度为n),模式串为t(长度为m)。扩展kmp作用是求extend数组。其中extent[i]代表t和s[i : n-1]的最长相同前缀。

    由于当存在extend[i] == m时,就起到kmp算法的功能,故又称扩展kmp。

    求extend数组需要求next数组作为辅助。这里next[i]代表t和t[i : m - 1]的最长相同前缀。假设next数组已经求出,下面求extend数组。

    设p为匹配过程中能达到的最右边界。设a为p对应位置,即extend[a] = p - a。假设包括i - 1前的extend数组都已经求出。那么首先,i的位置必定位于[a,p]区间之内。那么i位置对应在next上的位置为i - a。有两种情况:

    1. 如果i + next[i - a]小于p,那么可得extend[i] = next[i - a];
    2. 如果i + next[i - a] 大于等于p ,那么在直接在t[p - i]的基础上扩展就好了。注意是p - i而不是p - a,因为是在前缀的基础上扩展。然后更新p和a。

    求next数组其实就是t自己对自己求extend数组的过程。

    板子很好写

    int extend[N]; 
    int nt[N]; //next
    void getnext(char t[]) {
        int a = 0, p = 0;
        int len = strlen(t);
        nt[a] = len;
        for(int i = 1; i < len; i++) {
            if(i >= p || i + nt[i - a] >= p) { 
                if(i >= p) p = i;
                while(p < len && t[p] == t[p - i]) p++;
                nt[i] = p - i;
                a = i;
            } else {
                nt[i] = nt[i - a];
            }
        }
    }
    
    void search(char *s, char *t) {
        int a = 0, p = 0;
        int n = strlen(s);
        int m = strlen(t);
        for(int i = 0; i < n; i++) {
            if(i >= p || i + nt[i - a] > p) { // i >= p 的作用:举个典型例子,S 和 T 无一字符相同
                if(i >= p) p = i;
                while(p < n && p - i < m && s[p] == t[p - i]) p++;
                extend[i] = p - i;
                a = i;
            } else {
                extend[i] = nt[i - a];
            }
        }
    }
    
    
    

    相关题目

    kuangbin kmp专题

    扩展kmp:
    Clairewd’s message
    Period II
    Count the string

    参考

    https://oi-wiki.org/string/kmp/

  • 相关阅读:
    系统编程-进程-fork深度理解、vfork简介
    九鼎S5PV210开发板的SD卡启动、uboot tftp升级内核镜像
    jiffies相关时间比较函数time_after、time_before详解
    Java基础第六章(循环结构二)
    Java基础第五章(循环结构一)
    Java基础第四章(选择结构二)
    Java基础第三章(选择结构一)
    Java基础第二章(变量、数据类型和运算符)
    Java基础第一章
    HTML iframe的使用
  • 原文地址:https://www.cnblogs.com/limil/p/12695071.html
Copyright © 2011-2022 走看看