扩展KMP模板

扩展KMP:
    给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0 <= i < lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i + z - 1]==B[0 .. z - 1]的最大的z值)。
    扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。
【算法】
    设next[i]为满足B[i..i + z - 1] == B[0..z - 1]的最大的z值(也就是B的自身匹配)。
    设目前next[0..lenB - 1]与ex[0..i - 1]均已求出,要用它们来求ex[i]的值。
    设p为目前A串中匹配到的最远位置,k为让其匹配到最远位置的值(或者说,k是在0 <= i0 < i的所有i0值中,使i0 + ex[i0] - 1的值最大的一个,p为这个最大值,即k + ex[k] - 1),显然,p之后的所有位都是未知的,也就是目前还无法知道A[p + 1..lenA - 1]中的任何一位和B的任何一位是否相等。
    根据ex的定义可得,A[k..p] == B[0..p - k],因为i > k,所以又有A[i..p] == B[i - k..p - k],设L = next[i - k],则根据next的定义有B[0..L - 1] == B[i - k..i - k + L - 1]。
    考虑i - k + L - 1与p - k的关系:

(1)i - k + L - 1 < p - k,即i + L <= p。
        这时,由A[i..p] == B[i - k..p - k]可以得到A[i..i + L - 1] == B[i - k..i - k + L - 1],又因为B[0..L - 1] == B[i - k..i - k + L - 1]所以A[i..i + L - 1] == B[0..L - 1],这就说明ex[i] >= L。
        又由于next的定义可得,A[i + L]必然不等于B[L](否则A[i..i + L]==B[0..L],因为i+L<=p,所以A[i..i + L] == B[i - k..i - k + L],这样B[0..L] == B[i - k..i - k + L],故next[i - k]的值应为L + 1或更大),这样,可以直接得到ex[i] = L!

(2)i + k - L + 1 >= p - k,即i + L > p。
        这时,首先可以知道A[i..p]和B[0..p - i]是相等的(因为A[i..p] == B[i - k..p - k],而i + k - L + 1 >= p - k,由B[0..L - 1] == B[i - k..i - k + L - 1]可得B[0..p - i] == B[i - k..p - k],即A[i..p] == B[0..p - i]),然后,对于A[p + 1]和B[p - i + 1]是否相等,目前是不知道的(因为前面已经说过,p是目前A串中匹配到的最远位置,在p之后无法知道任何一位的匹配信息),因此,要从A[p + 1]与B[p - i + 1]开始往后继续匹配(设j为目前B的匹配位置的下标,一开始j = p - i + 1,每次比较A[i + j]与B[j]是否相等,直到不相等或者越界为止,此时的j值就是ex[i]的值)。在这种情况下,p的值必然会得到延伸,因此更新k和p的值。
   
    边界:ex[0]的值需要预先求出,然后将初始的k设为0,p设为ex[0]-1。
    对于求next数组,也是“自身匹配”,类似KMP的方法处理即可。唯一的不同点也在边界上:可以直接知道next[0] = lenB,next[1]的值预先求出,然后初始k = 1,p = ex[1]。
   
    严重注意:在上述的情况(2)中,本该从A[p + 1]与B[p - i + 1]开始匹配,但是,若p + 1 < i,也就是p - i + 1 < 0(这种情况是有可能发生的,当ex[i - 1] = 0,且前面的ex值都没有延伸到i及以后的时候)的话,需要将A、B的下标都加1(因为此时p必然等于i - 2,如果A、B的下标用两个变量x、y控制的话,x和y都要加1)!!


模板代码:

#include <iostream>
#include <cstring>

using namespace std;

const int n = 500004;
int next[n];
int extend[n];
char S[n];
char T[n];

void GetNext(char* T)
{
    int k = 0;
    int Tlen = strlen(T);
    next[0] = Tlen;
    while(k < Tlen - 1 && T[k] == T[k + 1])
        k++;
    next[1] = k;
    k = 1;
    for(int i = 2; i < Tlen; i++)
    {
        int p = k + next[k] - 1, L = next[i - k];
        if(i + L - 1 >= p)
        {
            int j = (p - i + 1) > 0 ? p - i + 1 : 0;
            while(i + j < Tlen && T[i + j] == T[j])
                j++;
            next[i] = j;
            k = i;
        }
        else next[i] = L;
    }
}

void GetExtend(char* S, char* T)
{
    int k = 0;
    GetNext(T);
    int Slen = strlen(S);
    int Tlen = strlen(T);
    int MinLen = Slen < Tlen ? Slen : Tlen;
    while(k < MinLen && S[k] == T[k])
        k++;
    extend[0] = k;
    k = 0;
    for(int i = 1; i < Slen; i++)
    {
        int p = k + extend[k] - 1, L = next[i - k];
        if(i + L - 1 >= p)
        {
            int j = (p - i + 1) > 0 ? p - i + 1 : 0;
            while(i + j < Slen && j < Tlen && S[i + j] == T[j])
                j++;
            extend[i] = j;
            k = i;
        }
        else extend[i] = L;
    }
}

时间: 2024-12-14 18:12:50

扩展KMP模板的相关文章

HDU 6153 A Secret(扩展KMP模板题)

A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others) Total Submission(s): 2523    Accepted Submission(s): 934 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,w

扩展kmp 模板

#include<iostream> #include<string> using namespace std; const int MM=100005; int next[MM],extand[MM]; char S[MM],T[MM]; void GetNext(const char *T){ int len=strlen(T),a=0; next[0]=len; while(a<len-1 && T[a]==T[a+1]) a++; next[1]=a;

HDU 2594 扩展kmp模板题

题目大意: 给定两个字符串,在第一个字符串中找到一个最大前缀作为第二个字符串的后缀 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <queue> 7 #include <climits> 8 #include <cmath>

[目前未找到题目]扩展KMP模板

procedure build_next; begin lena:=length(a);lenb:=length(b); next[0]:=lenb;next[1]:=lenb-1; for i:=1 to lenb-1 do if b[i]<>b[i+1] then begin next[1]:=i;break; end; k:=1; for i:=2 to lenb do begin p:=k+next[k]-1;L:=next[i-k]; if i+L<=p then next[i

KMP、扩展KMP、Manacher模板

推导过程推荐看这篇: KMP模板: 1 void init(){ 2 int j = nex[0] = -1, i = 0; 3 int len = strlen(str); 4 while(i < len){ 5 if(j == -1 || str[i] == str[j]){ 6 nex[++i] = ++j; 7 }else j = nex[j]; 8 } 9 } 10 int KMP(){ 11 int i = 0, j = 0, sum = 0; 12 int len = strlen

(模板)扩展kmp算法(luoguP5410)

题目链接:https://www.luogu.org/problem/P5410 题意:有两个字符串a,b,要求输出b与a的每一个后缀的最长公共前缀.输出: 第一行有lenb个数,为b的next数组(特别地,next1为lenb) 第二行有lena个数,即答案. 思路:扩展kmp模板,涉及字典树,后续再补,先放模板. AC code: #include<bits/stdc++.h> #define N 1000010 using namespace std; int q,nxt[N],exte

HDU 6153 A Secret(扩展kmp)

A Secret Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 256000/256000 K (Java/Others)Total Submission(s): 1530    Accepted Submission(s): 570 Problem Description Today is the birthday of SF,so VS gives two strings S1,S2 to SF as a present,wh

Part.5【马拉车&amp;扩展KMP】

Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n).网上对其介绍的资料已经挺多了的,请善用搜索引擎. 而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀[KMP更像是一个自动机] 题目: POJ 1159: Palindrome 求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度. 解法:直接求串S和反转串Sr的最长公共子序列. #include <cstdlib> #include <cstdio> #include <

KMP与扩展KMP

原文转自:http://www.cppblog.com/MatoNo1/archive/2011/04/17/144390.aspx KMP:给出两个字符串A(称为模板串)和B(称为子串),长度分别为lenA和lenB,要求在线性时间内,对于每个A[i] (0<=i<lenA),求出A[i]往前和B的前缀匹配的最大匹配长度,记为ex[i](或者说,ex[i]为满足A[i- z+1..i]==B[0..z-1]的最大的z值).KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置