poj 2752 Seek the Name, Seek the Fame

Description

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:)

Input

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.

Output

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

Sample Input

ababcababababcabab
aaaaa

Sample Output

2 4 9 18
1 2 3 4 5

题意分析

每次看懂题的意思,也要费一番周折
这个题目的Description说了一大堆,其实就是给定一个字符串,要你统计出来既是前缀,同时也是后缀的每个子串的长度
如题目中给的例子“alala”,既是前缀也是后缀的子串有三个‘a’、‘ala’和‘alala’,他们的长度分别是1、3、5
在给出的样例中,“ababcababababcabab”,这样的子串有4个:‘ab’、‘abab’、‘ababcabab’、整个串‘ababcababababcabab’,他们的长度分别是 2 4 9 18
另一个样例:‘aaaaa’,这样的子串有5个:‘a’、‘aa’、‘aaa’、‘aaaa’、‘aaaaa’,长度是1 2 3 4 5

解题思路

这个题需要用到KMP,先要把KMP研究清楚,这道题就迎刃而解了。KMP主要是用在两个字符串匹配比较的过程中,当发生失配的时候,为了减少不必要的比较次数而建立一个next数组。对于模式串的每一个字符的next值,是该字符前面子串的最大共同前后缀长度,这个next值也是当匹配到该字符失配时,在保持主串指针不动的情况下,模式串指针的下一个位置。

next值在本题中有很大用处。

如下图这个例子,一个字符串“ababcababababcabab”,一共18个字符,字符串长度len=18。字符串存放在w[0]-w[17]中,我们计算出每一个next值,放在next[0]-next[18]中,其中next[18]中存放的是整个字符串共同前后缀最大长度,为next[len]=9,也就是说这个字符串的前9个字符和后面9个字符是一样的,这正是我们要计算的最大的一个解(当然,如果整个字符串长度也算一个解的话,这个9就是倒数第二个解了)
在这里插入图片描述
那么,下一个解怎么计算呢?其实,next[18]=9,说明字符串左边9个字符和字符串右边9个字符一样,相当于把字符串分成了两个部分,左边红色的部分,和右边蓝色的部分(如上图),下一个稍短一点的前后缀怎么找呢?如果存在的话,必然是最左边长度比9短一点的若干个字符和最右边同样个数的字符相等,同时因为左边9个和右边9个又一样,因此我们只需要在左边9个中找这9个的前后缀最大长度即可。这个结果就存放在next[9]中,这就相当于第一次通过next[len]得到了第一个结果,然后通过 next[ next[len] ]得到了第二个结果,此例中next[9]=4,所以4是第二个结果,下一次next[4]=2,再下一次next[2]=0这个就不是了。因此最后这个题的结果就是2、4、9,再加上整个字符串的话,就是2、4、9、18。

程序如下:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define L2 400005

int next[L2], len2, ans[L2];
char p[L2];
bool hash[L2];

void get_next()
{
	int k = -1, j = 0;
	next[0] = -1;
	while (j < len2)
	{
		if (k == -1 || p[j] == p[k])
		{
			j++, k++;
			next[j] = k;
		}
		else k = next[k];
	}
}

int main()
{
	int k, i, j;
	while (scanf ("%s", p) != EOF)
	{
		k = 0;
		memset (hash, false, sizeof(hash));
		len2 = strlen (p);
		get_next();
		j = next[len2];
		while (j > 0)
			ans[k++] = j, j = next[j];
		for (i = k - 1; i >= 0; i--)		   //倒过来输出,就是从小到大了
			printf ("%d ", ans[i]);
		printf ("%d\n", len2);
	}
	return 0;
}

另外,在sdfzyhx的博客中,代码是下面这样的:

#include<cstdio>
#include<cstring>
char s[400010];
int next[400010],sta[400010];
int main()
{
	int i,j,k,l,m,n,p,q,x,y,z,top;
	char c;
	while (scanf("%s",s+1)==1)
	{
		memset(sta,0,sizeof(sta));
		l=strlen(s+1);

		for (i=2,j=0;i<=l;i++)
		{
			while (j>0&&s[j+1]!=s[i]) j=next[j];
			if (s[j+1]==s[i]) j++;
			next[i]=j;
		}
		top=0;
		for (i=l;i;i=next[i])
		  sta[++top]=i;
		while (top>1)
		  printf("%d ",sta[top--]);
		printf("%d\n",sta[1]);
	}
}

这个代码更简洁一些,不同的是,数组的下标都是从1开始,并且next[]也不一样。
next[i]的值,是s[1…i]中最大共同前后缀的长度。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值