KMP Next数组的应用入门

poj - 2751 

题意:找出一个字符串中既是前缀又是后缀的字符串的长度,增序输出。

思路:我们学习了KMP,KMP中next的数组的意思为:借用了一下kuangbin大佬的:

假设主串:S: S[1] S[2] S[3] ……S[n]

模式串:T: T[1] T[2] T[3]…..T[m]

现在我们假设主串第i 个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i 个字符与模式串的第k(k<j)个字符继续比较,此时就有S[i] != T[j]

主串:   S[1]...S[i-j+1]...S[i-1]S[i]...

                    ||(匹配)   ||    ≠

模式串:            T[1]...  T[j-1] T[j]

由此,可以得到关系式如下

  T[1]T[2]T[3]...T[j-1] = S[i-j+1]...S[i-1]

由于S[i] != T[j],接下来S[i]将与T[k]继续比较,则模式串中的前k-1咯字符串必须满足下列关系式,并且不可能存在k'>k满足下列关系式:

T[1]T[2]T[3]...T[k-1] = S[j-k+1]S[j-k+2]...S[i-1] (k<j)

也就是说:

主串:  S[1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

                    ||        ||         ||    ?(待比较)

模式串:           T[1]      T[2]...  T[k-1] T[k]

现在可以把前面的关系综合总结如下

S[1]...S[i-j+1]...S[i-k+1]S[i-k+2]...S[i-1]S[i]...

            ||          ||       ||           ||   ≠

           T[1]...    T[j-k+1] T[j-k+2]...   T[j-1] T[j]

                         ||       ||            ||    ?

                        T[1]     T[2] ...     T[k-1] T[k]

现在唯一的任务就是如何求k了,通过一个next函数求。


求Next的方法为:

void get_next(char *T)
{
    int len = strlen(T);
    Next[0] = -1;int i = 0,j = -1;
    while(i < len)
    {
        if(j == -1 || T[i] == T[j])
        {
            Next[++ i] = ++ j;
        }
        else j = Next[j];
    }
}


这样我们可以写一下这道题:

首先,字符串本身是字符串的前缀和后缀,我们假设不是本身的就是真子后缀。然后我们要看一下最长真子后缀是多少,当然是在len处匹配失败的值,即Next[len],因为在len处匹配失败,令k = Next[len],说明 S[0],s[1]……s[k - 1]和S[k ]……s[len - 1]对应相等,k为最长真子后缀。

同样的,此时长度为k的前缀和后缀相等,那么我们可以把一个刚刚表示的后缀当成前缀,相当于长度为k的前缀之中,因为下标是从0开始,所以在k处匹配失败,k' = Next[k],k'为第二小的真子后缀;

依次类推。


//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 4e5 + 10;
typedef long long ll;
#define clr(x,y) memset(x,y,sizeof x)
#define INF 0x3f3f3f3f
const ll Mod = 1e9 + 7;
typedef pair<int,int> P;


int Next[maxn];
char s[maxn],s2[maxn];

void get_next(char *T)
{
    int len = strlen(T);
    Next[0] = -1;int i = 0,j = -1;
    while(i < len)
    {
        if(j == -1 || T[i] == T[j])
        {
            Next[++ i] = ++ j;
        }
        else j = Next[j];
    }
}
vector<int>ans,ans2;
int main()
{
    while( ~ scanf("%s",s))
    {
        get_next(s);
        int len = strlen(s);
        ans2.clear();

        ans2.push_back(len);

        int i = len;
        while(Next[i] > 0)
        {
            ans2.push_back(Next[i]);
            i = Next[i];
        }
        for(int i = ans2.size() - 1;i >= 0; i --)
            printf("%d%c",ans2[i],i ? ' ' : '\n');
    }
    return 0;
}

poj2406

题意:找出字符串的最小循环节,输出循环次数。

思路:结论:如果存在循环节,那么len % (len - Next[len]) == 0,最小循环节长度为L = len - Next[len],循环次数为len/L;否则循环节就是本身。仔细思考,举几个例子,才明白。不会证明。


//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
#define clr(x,y) memset(x,y,sizeof x)
#define INF 0x3f3f3f3f
const ll Mod = 1e9 + 7;
typedef pair<int,int> P;


int Next[maxn];
char s[maxn],s2[maxn];

void get_next(char *T)
{
    int len = strlen(T);
    Next[0] = -1;int i = 0,j = -1;
    while(i < len)
    {
        if(j == -1 || T[i] == T[j])
        {
            Next[++ i] = ++ j;
        }
        else j = Next[j];
    }
}
int main()
{
    while( ~ scanf("%s",s) && s[0] != '.')
    {
        get_next(s);
        int len = strlen(s);
        if(len % (len - Next[len]) == 0)
            printf("%d\n",len / (len - Next[len]));
        else puts("1");
    }
    return 0;
}

poj 3461 kMP模板题


//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn = 1e6 + 10;
typedef long long ll;
#define clr(x,y) memset(x,y,sizeof x)
#define INF 0x3f3f3f3f
const ll Mod = 1e9 + 7;
typedef pair<int,int> P;



char s[maxn],t[maxn];
int Next[maxn];
void get_next(char* T)
{
    int i = 0,j = -1;Next[0]= -1;
    int len = strlen(T);
    while(i < len)
    {
        if(j == -1 || T[i] == T[j])
        {
            Next[++ i] = ++j;
        }
        else j = Next[j];
    }
}
int solve(char *S,char * T)
{
    get_next(t);
    int len1 = strlen(S),len2 = strlen(T);
    int i = 0,j = 0;
    int ans = 0;
    while(i < len1)
    {
        if(j == -1 || S[i] == T[j])
        {
            i ++;j ++;
        }
        else j = Next[j];
        if(j == len2)
            ans ++,j = Next[j];
    }
    return ans;
}
int main()
{
    int Tcase;scanf("%d",&Tcase);
    while(Tcase --)
    {
        scanf("%s%s",t,s);
        printf("%d\n",solve(s,t));
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/xiaolonggezte/article/details/79162562