HDU-3746 Cyclic Nacklace

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3746


题目大意:

给你一个字符串,要求将字符串的全部字符最少循环2次需要添加的字符数。

例子:

abcabc 已经循环2次,添加数为0

abcac 没有循环2次,添加字符abcac。数目为5.

abcabcab 已经循环过2次,但第三次不完整,需要添加数为1


解题思路:

next[]数组的运用。

这里需要深刻理解next数组的含义,所以需要花费一定的功夫去弄懂这些。。

对于next数组,经过3天的学习,有了一定的认识。自己总结一下吧。

首先,求next数组有两种方法。第一种是严蔚敏教授那本数据结构上的求法。代码如下:

void getnext(const char *s)
{
	int i = 0, j = -1;
	nextval[0] = -1;
	while(i != len)
	{
		if(j == -1 || s[i] == s[j])
			nextval[++i] = ++j;
		else
			j = nextval[j];
	}
}

这种求法的含义是:

next[i]代表了前缀和后缀的最大匹配的值(需要彻底明白这点,相当重要)

另外一种是对这种算法的改进版本,代码如下:

void getnext(const char *p) //前缀函数(滑步函数)
{
	int i = 0, j = -1;
	nextval[0] = -1;
	while(i != len)
	{
		if(j == -1 || p[i] == p[j]) //(全部不相等从新匹配 || 相等继续下次匹配)
		{
			++i, ++j;
			if(p[i] != p[j]) //abcdabce
				nextval[i] = j;
			else //abcabca
				nextval[i] = nextval[j];
		}
		else
			j = nextval[j]; //子串移动到第nextval[j]个字符和主串相应字符比较
	}
}

改进的地方在于-1的运用,关于怎么优化我已经写过了。自己看下就行了。。。但是一定要明白。

不同之处:

没有优化的getnext函数,next数组存的是前缀和后缀的最大匹配值,而优化后的getnext函数存的是在这个基础,进行更高效的改进。

比如abcabca

改进前最后一个字符next[7]=4,表示的是前缀和后缀最大匹配是4,即abca和abca。

改进后的next[7]=-1。这点也需要彻底搞懂,才能灵活的运用next函数。

总结一下:

在求前缀和后缀的最大匹配值时,要使用第一种,也就是未优化的算法。在运用KMP时,使用第二种算法,因为避免了多余的判断,更加高效。

YY:

其实我们可以发现KMP算法的精华部分是一个DP。每次右滑时,都是根据前面状态得到的有用信息进行的。相当于记忆化更新。这样算法才具有了很高的效率。

当然,理解这个算法确实需要仔细反复的研究,反正我是看了3天,才把这点基础东西搞的差不多,算是理解了80%吧。剩下的在以后做题的过程中慢慢发现吧。。。。


这道题的代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 100010

char s[N];
int nextval[N];
int len;

void getnext(const char *s)
{
	int i = 0, j = -1;
	nextval[0] = -1;
	while(i != len)
	{
		if(j == -1 || s[i] == s[j])
			nextval[++i] = ++j;
		else
			j = nextval[j];
	}
}

int main()
{
	int ncase;
	int length, add;
	scanf("%d", &ncase);
	while(ncase--)
	{
		scanf("%s", s);
		len = strlen(s);
		getnext(s);
		/*for(int i = 0; i <= len; ++i) //查看next数组的内容
			cout<<nextval[i]<<" ";
		cout<<endl;*/
		length = len - nextval[len]; //循环节的长度
		if(len != length && len % length == 0) //循环多次
			printf("0\n");
		else
		{
			add = length - nextval[len] % length; //取余的作用:abcab,去掉abc
			printf("%d\n",add);
		}
	}
	return 0;
}

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值