POJ2982 Seek the Name, Seek the Fame (KMP的next数组理解)

该篇博客探讨了一道编程题目,即寻找字符串的所有相同前后缀长度。通过介绍KMP算法的next数组,博主解释了如何在O(n)的时间复杂度内求解这一问题。文章详细阐述了next数组的本质,即寻找最长前后缀,并展示了如何通过回溯next数组来找出所有可能的前后缀长度。博主提供了详细的解题思路和C++代码实现,帮助读者理解KMP算法在解决此类问题上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:POJ2982 Seek the Name, Seek the Fame

描述

The little cat is so famous, that many couples tramp over hill and dale to Byteland, and asked the little cat to give names to their newly-born babies. They seek the name, and at the same time seek the fame. In order to escape from such boring job, the innovative little cat works out an easy but fantastic algorithm:

Step1. Connect the father’s name and the mother’s name, to a new string S.
Step2. Find a proper prefix-suffix string of S (which is not only the prefix, but also the suffix of S).

Example: Father=‘ala’, Mother=‘la’, we have S = ‘ala’+‘la’ = ‘alala’. Potential prefix-suffix strings of S are {‘a’, ‘ala’, ‘alala’}. Given the string S, could you help the little cat to write a program to calculate the length of possible prefix-suffix strings of S? (He might thank you by giving your baby a name:)

输入

The input contains a number of test cases. Each test case occupies a single line that contains the string S described above.

Restrictions: Only lowercase letters may appear in the input. 1 <= Length of S <= 400000.

输出

For each test case, output a single line with integer numbers in increasing order, denoting the possible length of the new baby’s name.

样例输入

ababcababababcabab
aaaaa

样例输出

2 4 9 18
1 2 3 4 5

解题思路

这题是要求解所有的前缀和后缀相同的情况,如果采用暴力求解,时间复杂度是O(n2)。采用KMP的next数组求解,时间复杂度是O(n)。要知道为什么用 KMP的next数组求解,首先要理解题意和KMP的next数组的本质。
KMP的next数组的本质其实是在模式串匹配到某一个字符失败的时候,是否要进行回溯,回溯多少个字符的一种策略。在朴素的模式匹配中,当主串匹配到第i个字符,模式串匹配到第j个字符,此时出现失败,则从主串从第i-j+1个字符,模式串从第0个字符重新开始匹配。这种算法其实回溯了j-1个字符,在这j-1个字符中,有很多是明显错误的,而这些错误其实可以提前用一定的算法获取到这些信息,这就是next数组。next数组告诉你当匹配失败时,需要回溯到哪一个位置。
next数组的理解,可以认为是寻找一个最长前后缀,就是最长的相同前缀和后缀。这个最长的相同前后缀,就是匹配失败时不需要回溯的部分,所以next数组保存的就是这个最长前后缀的长度。
更多关于KMP的理解,参考:数据结构KMP算法配图详解(超详细)
明白了next数组的本质,那么在本题中,我们要求解的是所有相同的前后缀长度,而next数组只保存了最长前后缀,如何求解?到这一步,其实还要明白next数组的算法过程。next数组在计算的时候,是从前往后计算的,后面的next取值其实依赖于前面相同元素的next取值,而前面相同元素的next值,保存的正式每一个相同前后缀的长度。
所以本题的解法:只需将整个串S当作模式串,求解S的next数组,最后的next[S.length()]就保存了最长的相同前后缀长度,对nextnext[S.length()]进行回溯,求解每一个前后缀的长度。

代码
#include <iostream>
#include <string.h>
#include <stack>

using namespace std;
#define MAX 400010

char data[MAX];
int next[MAX];

int main()
{
    while(cin>>data)
    {
        int len=strlen(data);

        //以下部分是求解next数组
        int j=0,k=-1;
        ::next[0]=-1;
        while(j<len)
        {
            if(k==-1||data[j]==data[k])
                ::next[++j]=++k;
            else k=::next[k];
        }

        //以下部分是对前后缀长度进行回溯
        stack<int> ans;
        while(len>0)
        {
            ans.push(len);
            len=::next[len];
        }


        while(!ans.empty())
        {
            cout<<ans.top()<<" ";
            ans.pop();
        }
        cout<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值