poj 2752

题目概述

给定一个字符串str,求所有符合条件的前缀长度,使此长度的前缀与等长的后缀匹配

时限

2000ms/6000ms

输入

每行一个字符串,输入到EOF为止

限制

1<=字符串长度<=4e5

输出

每行若干个数,为符合条件的前缀长度按升序排列,每个数字后跟一空格

样例输入

ababcababababcabab
aaaaa

样例输出

2 4 9 18
1 2 3 4 5

讨论

字符串,kmp算法中next数组的应用,只要知道next数组中每个值的意义是由其前面的字符构成的子串中,互相匹配的最长前缀后缀的长度,也就是这个题所求的东西,比如第一组样例,从最后一个字符开始看next数组(假设已经求得next数组),字符串长18,next[18]为9,说明整个字符串最长长度为9的前缀和后缀是匹配的,下面看看有没有短一点的,既然长度9的前缀后缀都一样,那整个字符串的其他前缀也就是长度9的前缀的前缀,整个字符串的其他后缀也就是长度9的后缀的后缀,也就是长度9的前缀的后缀,好绕口……但确实如此,因而接下来只要考察长度9的前缀(其实考察后缀也一样)即可,长度9,next[9]为4,字符串被分成三部分,前缀,长度4,中间没用的部分,长度1,后缀,长度4,然后遵循之前的思想,继续递归找下去,直到某个next的值为0为止,最后输出
等一下,为什么next数组的意义是这个?实际上因为kmp的思想就是这个,每次不要让模式串的指针都回溯到头,所以需要找到模式串自己和自己匹配的长度,确切说是对于模式串所有前缀,找出(前缀的)最长的前缀,使其与(前缀的)这个长度的后缀匹配,当然,找到的长度自然不能(也不可能)是自身长度,这也就是为什么上面的例子中next[18]为9,而不是next[17](str[17]是字符串的最后一个非空白字符,str[18]是\0),所以说,人家算法就这么设计的,意义自然是这个

题解状态

2632K,125MS,C++,702B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>//这些都是计算几何的遗留产物 不用管他们
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 400003
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-8

int Next[MAXN];//next数组 不是next因为iterator头文件中有函数next 使得迭代器向容器后方移动若干个位置 真不巧
char str[MAXN];//string 原始字符串
int stk[MAXN], top;//stack 数组模拟栈及其栈顶 因为要升序输出
void calNext(int len)//calculate_next 额也不明白其正确性 不过所幸不难背过 一般也不会对这个函数下手
{
    int p = 0, i = -1;
    Next[0] = -1;
    while (p < len)
        if (i == -1 || str[p] == str[i])
            Next[++p] = ++i;
        else
            i = Next[i];
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    while (~scanf("%s", str)) {//input
        int len = strlen(str);//读取长度备用
        calNext(len);
        while (len) {
            stk[top++] = len;//首先字符串自身的长度必然符合条件 但是next数组中不会有这个值 因而先把自己压栈
            len = Next[len];//然后再将字符串划分寻找较小的前缀长度
        }
        for (; top; top--)
            printf("%d ", stk[top - 1]);//output//退栈输出
        printf("\n");//output
    }
}

EOF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值