KMP / EXKMP

目录

KMP

字符串匹配

最小循环覆盖 

EXKMP

字符串匹配

Secret word 

Password 


KMP

模板

void kmp() {
    int m = strlen(p + 1);
    int j = 0;
    nxt[1] = 0;
    for (int i = 2; i <= m; ++i) {
        while (j && p[i] != p[j + 1])
            j = nxt[j];
        if (p[i] == p[j + 1])
            ++j;
        nxt[i] = j;
    }
}

p[i]  与  p[j + 1]  作比较? 

j是与i - 1的字符相匹配的。所以但我们计算i时,待匹配的是j + 1 这个位置的字符。

比如下方的第二个和第四个字符,都是b,他们是匹配的。i等于5之后,此时的j还是2,所以是i与j+1进行比较。

用来解决字符串的匹配问题,比如在文档中  Ctrl + F 去查找特定的内容。

需要清除nxt数组的含义:

                a        b        a        b        a        c        m

                0        0        1        2        3        0        0

nxt[i]  以i结尾的字符,与p字符串前缀最大匹配的长度

字符串匹配

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back

char s[100005], p[100005];
int nxt[100005], f[100005];
int n, m;
void solve() {

    scanf("%s%s", s + 1, p + 1);

    n = strlen(s + 1);
    m = strlen(p + 1);
    int j = 0;
    nxt[1] = 0;

    for (int i = 2; i <= m; ++i) {
        while (j && p[i] != p[j + 1])
            j = nxt[j];
        if (p[i] == p[j + 1])
            ++j;
        nxt[i] = j;
    }
    
    j = 0;
    for (int i = 1; i <= n; ++i) {
        while (j == m || (j && s[i] != p[j + 1]))
            j = nxt[j];
        if (s[i] == p[j + 1])
            ++j;
        f[i] = j;
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        if (f[i] == m)
            ans++;
    }
    if (ans == 0) printf("-1\n-1\n");
    else {
        printf("%d\n", ans);
        for (int i = 1; i <= n; ++i) {
            if (f[i] == m) {
                printf("%d ", i - m + 1);
            }
        }
        puts("");
    }
}
int main(){
    int t; scanf("%d", &t);
    for (int i = 1; i <= t; ++i) {
        solve();
    }   
    return 0;
}

最小循环覆盖 

结论:m - nxt[m] 

nxt[m] 是以m为结尾的字符,与字符串p前缀匹配的最大长度。 

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back

char p[100005];
int nxt[100005];
void kmp() {
    int m = strlen(p + 1);
    int j = 0;
    nxt[1] = 0;
    for (int i = 2; i <= m; ++i) {
        while (j && p[i] != p[j + 1])
            j = nxt[j];
        if (p[i] == p[j + 1])
            ++j;
        nxt[i] = j;
    }
    printf("%d\n", m - nxt[m]);
}
int main(){
    scanf("%s", p + 1);
    kmp();
    return 0;
}

EXKMP

拓展kmp,又称Z算法

模板:

void exkmp() {
    int n = strlen(s + 1);
    z[1] = 0;
    int L = 1, R = 0;
    for (int i = 2; i <= n; ++i) {
        if (i > R) {
            z[i] = 0;
        } else {
            int k = i - L + 1;
            z[i] = min(z[k], R - i + 1);
        }
        while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]])
            ++z[i];
        if (i + z[i] - 1 > R) {
            L = i, R = i + z[i] - 1;
        }
    }
}

z[i]  : 从i开始的字符串与s字符串前缀最大匹配的长度

字符串匹配

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 200005;
char s[N], p[N];
int z[N];
int n, m;

void solve() {
    scanf("%s%s", s + 1, p + 1);
    n = strlen(s + 1);
    m = strlen(p + 1);
    p[m + 1] = '#';
    for (int i = m + 2, j = 1; j <= n; ++i, ++j) {
        p[i] = s[j];
    }
    
    z[1] = 0;
    int L = 1, R = 0;
    for (int i = 2; i <= n + m + 1; ++i) {
        if (i > R) {
            z[i] = 0;
        } else {
            int k = i - L + 1;
            z[i] = min(z[k], R - i + 1);
        }
        while (i + z[i] <= n + m + 1 && p[z[i] + 1] == p[i + z[i]])
            ++z[i];
        if (i + z[i] - 1 > R) {
            L = i, R = i + z[i] - 1;
        }
    }

    int ans = 0;
    for (int i = m + 2; i <= n + m + 1; ++i) {
        if (z[i] == m) ans++;
    }
    if (ans == 0) puts("-1\n-1");
    else {
        printf("%d\n", ans);
        for (int i = m + 2; i <= n + m + 1; ++i) {
            if (z[i] == m) {
                printf("%d ", i - m - 1);
            }
        }
        puts("");
    }
}
int main(){
    int t; scanf("%d", &t);
    while (t--) {
        solve();
    }
    return 0;
}

Secret word 

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back

const int N = 1E5 + 5;
char s[N << 1];
int z[N << 1];

void solve() {
    int n = strlen(s + 1);
    s[n + 1] = '#';

    for (int i = n + 2, j = n; j ; --j, ++i) {
        s[i] = s[j];
    }

    z[1] = 0;
    int L = 1, R = 0;
    for (int i = 2; i <= 2 * n + 1; ++i) {
        if (i > R) {
            z[i] = 0;
        } else {
            int k = i - L + 1;
            z[i] = min(z[k], R - i + 1);
        }
        while (i + z[i] <= 2 * n + 1 && s[z[i] + 1] == s[i + z[i]])
            ++z[i];
        if (i + z[i] - 1 > R) {
            L = i, R = i + z[i] - 1;
        }
    }
    int ans = 0;
    for (int i = n + 2; i <= 2 * n + 1; ++i) {
        ans = max(ans, z[i]);
    }
    for (int i = ans; i >= 1; --i)
        printf("%c", s[i]);
    putchar(10);
}
int main(){
    scanf("%s", s + 1);
    solve();

    return 0;
}

Password 

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 1000005;
char s[N], p[N];
int z[N];
int n, m;

void solve() {
    scanf("%s", s + 1);
    n = strlen(s + 1);
    
    z[1] = 0;
    int L = 1, R = 0;
    for (int i = 2; i <= n; ++i) {
        if (i > R) {
            z[i] = 0;
        } else {
            int k = i - L + 1;
            z[i] = min(z[k], R - i + 1);
        }
        while (i + z[i] <= n && s[z[i] + 1] == s[i + z[i]])
            ++z[i];
        if (i + z[i] - 1 > R) {
            L = i, R = i + z[i] - 1;
        }
    }

    int ans = 0, x = 0;
    for (int i = 1; i <= n; ++i) {
        if (z[i] == n - i + 1) {
            if (x >= z[i])
                ans = max(ans, z[i]);
        }
        x = max(x, z[i]);
    }
    if (!ans) printf("Just a legend\n");
    else {
        for (int i = 1; i <= ans; ++i)
            printf("%c", s[i]);
        puts("");
    }
}
int main(){
    solve();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xingxg.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值