欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

KMP中next数组的理解

程序员文章站 2022-03-31 21:17:48
...

本文参考于:https://www.cnblogs.com/lfri/p/10341479.html

一、next数组遍历整个字符串,next[i]表示在i前即[0, i-1]中前后缀相同的最大长度;

  最简单你可以从 next 数组中直接得到,母串的最长公共前后缀串的长度,出题人可以在这上面做一下文章,比如说下面这个题目,让你再判断一下这个前后缀串是不是在母串中间出现过一次:

CodeForces - 126B Password
Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. However, its doors were firmly locked and even Obelix had no luck opening them.

A little later they found a string s, carved on a rock below the temple’s gates. Asterix supposed that that’s the password that opens the temple and read the string aloud. However, nothing happened. Then Asterix supposed that a password is some substring t of the string s.

Prefix supposed that the substring t is the beginning of the string s; Suffix supposed that the substring t should be the end of the string s; and Obelix supposed that t should be located somewhere inside the string s, that is, t is neither its beginning, nor its end.

Asterix chose the substring t so as to please all his companions. Besides, from all acceptable variants Asterix chose the longest one (as Asterix loves long strings). When Asterix read the substring t aloud, the temple doors opened.

You know the string s. Find the substring t or determine that such substring does not exist and all that’s been written above is just a nice legend.

Input
You are given the string s whose length can vary from 1 to 106 (inclusive), consisting of small Latin letters.

Output
Print the string t. If a suitable t string does not exist, then print “Just a legend” without the quotes.

Examples
Input
fixprefixsuffix
Output
fix
Input
abcdabc
Output
Just a legend

题意

  给你一个字符串,让你在里面找出一个最长的公共前后缀串,并且这个串在母串中间还出现过一次,如果不存在,就输出 “Just a legend” 。

思路

  nxet[len],就是母串的最长公共前缀后缀串的长度,只需要判断这个串(或者是它的子串)是否在母串中间出现过,然后输出即可。
   例如:acacacacac 输出的是 acacac
  next[i] 表示在 i 前即 [0,i-1]中前后缀相同的最大长度;假设 p 长度是 lp,则 next[lp] 就是前后缀相同的字符串的长度;所以说,我们首先要看看是否存在公共前缀后缀,
如果有(ans>0),这只是保证了可能有解,因为我们还要看中间是否出现过,这个时候,我们让 ans=next[ans],继续看这个 next[ans] 是否出现过,为什么呢?因为你每往前移动,就相当于产生了一个可能的答案,但是我们需要中间也出现过,所以就要判断这个next[ans]值是否出现过,当出现过的就是答案。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<map>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 1000101;
int nxt[maxn],has[maxn];
char p[maxn];
void get_nxt(int pl)
{
    int i,j;
    i = 0;
    j = nxt[0] = -1;
    while(i<pl){
        if(j==-1||p[i]==p[j]){
            i++,j++;
            nxt[i] = j;
        }
        else
            j = nxt[j];
    }
}
int main(void)
{
    int lp;
    scanf("%s",p);
    memset(has,0,sizeof(has));
    lp = strlen(p);
    get_nxt(lp);
    for(int i=1;i<lp;i++){
        has[nxt[i]] = 1;
    }
    int ans = nxt[lp];
    while(!has[ans]){	//不断往前找,只到最简
        ans = nxt[ans];
        if(ans==-1) break;	//不加 TL
    }
    if(ans<=0)  printf("Just a legend\n");
    else{
        for(int i=0;i<ans;i++)
            printf("%c",p[i]);
        puts("");
    }
    return 0;
}

二、周期性字符串

1、周期型字符串 ⇔ n % (n-next[n])== 0 && next[n] != 0,循环节长度是 n-next[n]。
KMP中next数组的理解
2、next数组往前跳的步长是一样的,除了最后一次。即i−next[i]保持恒定。
KMP中next数组的理解
HDU - 1358 Period

For each prefix of a given string S with N characters (each character has an ASCII code between 97 and 126, inclusive), we want to know whether the prefix is a periodic string. That is, for each i (2 <= i <= N) we want to know the largest K > 1 (if there is one) such that the prefix of S with length i can be written as A K , that is A concatenated K times, for some string A. Of course, we also want to know the period K.

Input
The input file consists of several test cases. Each test case consists of two lines. The first one contains N (2 <= N <= 1 000 000) – the size of the string S. The second line contains the string S. The input file ends with a line, having the number zero on it.

Output
For each test case, output “Test case #” and the consecutive test case number on a single line; then, for each prefix with length i that has a period K > 1, output the prefix size i and the period K separated by a single space; the prefix sizes must be in increasing order. Print a blank line after each test case.

Sample Input
3
aaa
12
aabaabaabaab
0

Sample Output
Test case #1
2 2
3 3

Test case #2
2 2
6 2
9 3
12 4

题意

  输入长度为 n 的字符串,直到 n==0 时,输入结束;找出字符串的所有循环节长度和个数

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<map>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 1000101;
int nxt[maxn];
char p[maxn];
void get_nxt(int pl)
{
    int i,j;
    i = 0;
    j = nxt[0] = -1;
    while(i<pl){
        if(j==-1||p[i]==p[j]){
            i++,j++;
            nxt[i] = j;
        }
        else
            j = nxt[j];
    }
}
int main(void)
{
    int lp;
    int cas = 0;
    while(scanf("%d",&lp)&&lp){
        scanf("%s",p);
        get_nxt(lp);
        printf("Test case #%d\n",++cas);
        for(int i=1;i<=lp;i++){
            if(i%(i-nxt[i])==0&&nxt[i]!=0)
                printf("%d %d\n",i,i/(i-nxt[i]));
        }
        printf("\n");
    }
    return 0;
}