KMP(模板)

算法讲解: KMP算法最浅显易懂
模板来源: 从头到尾彻底理解KMP

例题:2018 UESTC Training for Search Algorithm & String——L主楼

题意:求字符串中的最短循环节,并输出该循环节

KMP最小循环节、循环周期:
定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。
(1)如果len可以被len - next[len]整除,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。
(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L,L=len-next[len]。

注意这里的len是原字符串的len+1;

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int T, len;
char s[100000 + 5];
int nex[100000 + 5];

void get_next(char* p, int next[]){
    int pLen = strlen(p) + 1;//要求循环节长度时这里才+1
    next[0] = -1;
    int k = -1;
    int j = 0;
    while (j < pLen - 1){  
        if (k == -1 || p[j] == p[k]){
            ++j;++k;
            if (p[j] != p[k])
                next[j] = k;
            else
                next[j] = next[k];
        }
        else{
            k = next[k];
        }
    }
}

int kmp(char* s, char* p){
    int i = 0;
    int j = 0;
    int sLen = strlen(s);
    int pLen = strlen(p);
    while (i < sLen && j < pLen){
            //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
        if (j == -1 || s[i] == p[j]){
            i++;
            j++;
        }
        else{
            //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
            //next[j]即为j所对应的next值        
            j = nex[j];
        }
    }
    if (j == pLen)
        return i - j;
    else
        return -1;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        memset(nex, 0, sizeof(nex));
        scanf("%d", &len);
        scanf("%s", s);
        len = strlen(s);
        get_next(s, nex);
        printf("%d\n", len - nex[len]);
        for (int i = 0; i < len - nex[len]; i++)
            printf("%c", s[i]);
        printf("\n");
    }
    return 0;
}

HDU1711(数字KMP)

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int T, len;
int len1, len2;
int s1[1000000 + 5];
int s2[1000000 + 5];
int nex[1000000 + 5];

void get_next(int* p, int next[]) {
    int pLen = len2;//要求循环节长度时这里才+1
    next[0] = -1;
    int k = -1;
    int j = 0;
    while (j < pLen - 1) {
        if (k == -1 || p[j] == p[k]) {
            ++j; ++k;
            if (p[j] != p[k])
                next[j] = k;
            else
                next[j] = next[k];
        }
        else {
            k = next[k];
        }
    }
}

int kmp(int* s, int* p) {
    int i = 0;
    int j = 0;
    int sLen = len1;
    int pLen = len2;
    get_next(s2, nex);
    while (i < sLen && j < pLen) {
        //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++      
        if (j == -1 || s[i] == p[j]) {
            i++;
            j++;
        }
        else {
            //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]      
            //next[j]即为j所对应的next值        
            j = nex[j];
        }
    }
    if (j == pLen)
        return i - j;
    else
        return -1;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d %d", &len1, &len2);
        for (int i = 0; i < len1; i++)
            scanf("%d", &s1[i]);
        for (int i = 0; i < len2; i++)
            scanf("%d", &s2[i]);
        if (kmp(s1,s2) == -1)
            printf("-1\n");
        else
            printf("%d\n", kmp(s1,s2)+1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38995588/article/details/80650721
今日推荐