KMP算法Next数组的应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_23320955/article/details/79115564

题目一:Count the string

思路

给定一个字符串,输出这个字符串的前缀出现次数,根据next的性质知,当next有值的时候,就证明肯定有一个匹配,所以我们统计出next数组里面>0的个数就是有多少个匹配,再加上与自身的匹配n就是答案

代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 200000 + 5;
int mod = 10007;
int len,nex[maxn];
char a[maxn];

void getnext()
{
    int i = 0, j = -1;
    nex[0] = -1;

    while(i < len) {
        if(j == -1 || a[i] == a[j]){
            nex[++i] = ++j;
        }
        else j = nex[j];
    }
}

int main()
{
    int t;
    cin >> t;
    while(t--) {
        scanf("%d%s", &len, a);
        getnext();
 //匹配个数+字符串长度
        int ans  = len;
        for(int i = 1; i <= len; i++) {
            int tmp = nex[i];
            while(tmp) {
                ans = (ans+1) % mod;
                tmp = nex[tmp];
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}



题目二:Power Strings


中文题意

题目输入若干个字符串,对于每个字符串求最短的循环节,并输出循环节的长度,若没有循环节则输出1。输入一个“ .” 表示输入结束。

思路
因为nex的定义是最长的前缀等于后缀,所以如果存在循环节时,len-nex[len]就一定是最短的那个循环节的长度,当len%(len-nex[len])==0时,说明存在循环节,否则就不存在,

证明如下: 

假如 nex[len] 位置 x 时 当 len%(len-nex[len])==0 时,那么 len%(len-nex[len]-a.len-b.len)==0(因为a.len=b.len=len-nex[len]),所以中间重叠的部分也可以被分为长度为 len-nex[len] 的字符串,此时a1==b2 b2==b3 a3==b4 

又因为 a==b可以推出 a1==b1==a2==a3==a4==b2==b3==b4。 
所以len-nex[len]为循环节的长度。 
假若 len%(len-nex[len])!=0 ,由以上可以看出中间重叠的部分不能正好分成长度为 len-nex[len] ,因此也不可能出现循环节。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 1E6 + 5;
char str[maxn];
int nex[maxn], len;

void getnext()
{
    int i = 0, j = -1;
    memset(nex, 0, sizeof(nex));
    nex[0] = -1;
    while(i < len) {
        if(j == -1 || str[i] == str[j])
            nex[++i] = ++j;
        else
            j = nex[j];
    }
}

int main()
{
    while(scanf("%s", str) != EOF) {
        if(str[0] =='.') break;
        len = strlen(str);
        getnext();
    //例如 aaaaa len = 4, len - nex[len] = 1
        if(len % (len - nex[len]) == 0) cout << len / (len - nex[len]) << endl;
        else cout << "1" << endl;
    }
    return 0;
}

题目三:Seek the Name, Seek the Fame


思路

next[i]表示 前i位中,前缀 、后缀中最大公共子串的长度

可以知道next[n],也就是最长的公共子串长度,然后就可以枚举长度了。 
然后用两个字符串数组tmp1,tmp2,分别表示前缀和后缀,然后一比较就好了,不过最初的想法比较朴素,又分别用两个函数每次重新实现,不过对于前缀来说每次加一位就好了,但是后缀就不怎么好弄了。经高人指导,用了strncmp就可以很简单的实现构造后缀 


代码

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

const int maxn = 400000 + 5;
char str[maxn];
int nex[maxn], ans[maxn], len;

void getnext()
{
    int i=0,j=-1;
    nex[0]=-1;

    while(i<len)
    {
        if(j==-1||str[i]==str[j])
        {
            nex[++i] = ++j;
        }
        else
            j=nex[j];
    }
}

int main()
{
    while(scanf("%s", str) != EOF) {
        int cnt = 0;
        len = strlen(str);
        getnext();

        int last = nex[len];
        while(last != -1) {
            ans[cnt++] = last;
            last = nex[last];
        }

        for(int i = cnt - 2; i >= 0; i--) {
            printf("%d ", ans[i]);
        }
        cout << len << endl;
    }
    return 0;
}




猜你喜欢

转载自blog.csdn.net/qq_23320955/article/details/79115564