kmp深入解析及应用

这个算法用来干嘛

对于一个模板串M和一个子串S,n=|M|,m=|S|。

定义tend[i]为一个最大的k使得S[1..k]=M[i-k+1..i]。

也就是说M从第i位开始往前能与S的前缀最大匹配的长度。

M= a a b b a a b a a b a

S= a a b a a b b

如上例 tend[7]=3

KMP算法就是在线性的时间复杂度内计算tend[1..n]。

算法实现

在此之前,假设我们已经计算好了一个辅助数组next[1..m]

同样的,next[i]表示一个最大的k(k

代码模板

裸题hihocoder1084

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i=a; i<=b; i++)
#define drep(i, a, b) for (int i=a; i>=b; i--)
#define inf 1e9
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
char S[maxn], P[maxn], next[maxn];
//P是模式串,S是匹配串
struct KMP {
    int n, m, next[maxn], tend[maxn];//tend[i]表示匹配串从第i位开始往前能与S的前缀最大匹配的长度
    int ans=0;

    void makeNext() {
        int q, k;
        next[0] = 0;
        for (q = 1, k = 0; q < m; q++) {
            while (k > 0 && P[q] != P[k]) k = next[k - 1];
            if (P[q] == P[k]) k++;
            next[q] = k;
        }
    }

    void init() {
        n=strlen(S), m=strlen(P);
        makeNext();
        ans=0;
    }

    void kmp() {
        init();
        for (int i=0, q=0; i<n; i++) {
            while (q>0 && P[q]!=S[i]) q=next[q-1];
            if (P[q] == S[i]) q++;
            tend[i]=q;
            if (q==m) {
                ans++;
                //printf("Pattern occurs with shift:%d\n",(i-m+1));
                //不可重叠加上 q=0;
            }
        }
    }
}T;

int main() {
    int kase;
    scanf("%d", &kase);
    while (kase--) {
        scanf("%s%s", P, S);
        T.kmp();
        printf("%d\n", T.ans);
    }
}

next数组的其它应用

next数组的一个妙用在于解决循环节问题,再此类问题中,其实不用完整做kmp,只需要求出next数组就够了

poj1961

题目大意: 求这个字符串到i为止有多少个循环串

核心代码

        for (int i = 0; i < n; i++) {
            int k=i-T.next[i]+1;
            if ((i+1)%k==0 && (i+1)!=k) printf("%d %d\n", i+1, (i+1)/k);
        }

例如一个字符串的第100为指向第95位,也就是说4-100位和前1-96位是匹配的,就是说96到100与91到95是是匹配的,同理87-90与91-95是匹配的…依次类推,若100能整除4,则4即为一个循环节长度

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i=a; i<=b; i++)
#define drep(i, a, b) for (int i=a; i>=b; i--)
#define inf 1e9
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
char S[maxn], P[maxn], next[maxn];
//P是模式串,S是匹配串
struct KMP {
    int n, m, next[maxn], tend[maxn];//tend[i]表示匹配串从第i位开始往前能与S的前缀最大匹配的长度
    int ans=0;

    void makeNext() {
        int q, k;
        next[0] = 0;
        for (q = 1, k = 0; q < m; q++) {
            while (k > 0 && P[q] != P[k]) k = next[k - 1];
            if (P[q] == P[k]) k++;
            next[q] = k;
        }
    }

    void init() {
        m = strlen(P);
        makeNext();
        ans = 0;
    }
}T;

int main() {
    freopen("in.txt", "r", stdin);
    int kase=0, n;
    while (scanf("%d", &n)!=EOF && n) {
        scanf("%s", P);
        T.init();
        printf("Test case #%d\n", ++kase);
        for (int i = 0; i < n; i++) {
            int k=i-T.next[i]+1;
            if ((i+1)%k==0 && (i+1)!=k) printf("%d %d\n", i+1, (i+1)/k);
        }
        printf("\n");
    }
}

7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7
7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值