2016.8.6 学习总结

manachar

	补上那天的manachar。
	manachar是一种能够快速找到字符串中的回文串的一种处理字符串的方法。
	核心思想就是:通过回文串是左右对称的这样的一个绝对成立的道理。能够通过左边已经求出的对称中心的数据直接赋值给完全相同的右边的对称中心,这就能节省较多的时间,从而使时间复杂度降了一维。与此同时,回文中心和边界要尽量往右边(远处)延伸。在我完全理解了这种算法后,我觉得也不过就是暴力的一种剪枝罢了。
	想了想,先不给出manachar的模板。
	manachar还有一个比较重要的细节。那就是,当没有对称中心,也就是回文串的字符个数为偶数个的时候,该怎么赋值呢??
	其实也是一样的道理,这时候只需要把字符串的两边以及每个字符之间的间隔里面插入一个‘#’或者是其他题目之外的字符。但最好不要是ASCII码的第0个。因为在char数组中要使用strlen()来得出字符串长度时,就会报错。因为strlen()是根据顺次寻找,直到找到ASCII码的第0个就结束查找。所以最好不要用ASCII的第0个。
	现在就一定保证有对称中心了,那就怎么办呢?
	给模板lor。
 
	1.读入
	2.用插入‘#’的方式处理字符串
	3.用manachar算法暴力判断字符串
		a.如果现在询问的对称中心在大回文中心的左边,那就暴力查找,得出数据
		b.如果现在询问的对称中心在大回文中心的右边,那就直接使用左边的数据
		c.如果现在询问的对称中心的最右端超过原回文中心的最右端,那就更新回文中心为现在询问的字符串,同时处理新回文中心的边界。
	4.得出整个manachar数据,可以用线性的时间得出所有答案
 
	其实manachar也是一种很水的算法嘛,并没有什么难度。
	那么我们就来分析一下它的时间复杂度,以及它为什么可以这么快【地心引力根本拉不住 。
	
	有人一定想要问。有没有可能每次都像暴力一样,因为暴力的查找而达到N^2的时间复杂度呢??
	too young!
	要解释清楚,就必须得配合一段程序来解释。
 
	int bj=0; // bj就是边(bian)界(jie)的意思
<span style="white-space:pre">	</span>
	int id=0; // id就是当前最大的对称中心
	size=x;
	for (int i=1;i<=size;i++)
	{
		if (bj>i) 
			if (p[2*id-i]<bj-i)
				p[i]=p[2*id-i];
			else p[i]=bj-i;
		else p[i]=1;
		while(now[i-p[i]]==now[i+p[i]]) p[i]++;
		if (i+p[i]>bj)
		{
			bj=i+p[i];
			id=i;
		}
	}
	
	有了这段程序,就可以看到,中间的while里面,虽然跟暴力的一样,外面的for也跟暴力的差不多。
有喷子肯定要说:你这个【消音】这两个嵌套起来不就是N^2的时间复杂度了嘛?但是我们从整体的角度来看。
中间的while每执行一次,里面的边界就会扩大一次,然而这只会在左边这样执行。到了对称中心的右边,
它之前执行过的就不会再次查找,而是可以通过大的回文中心来直接得出答案。这不就很少了嘛?
喷子:“那么,你bibi了这么久,时间复杂度到底是多少?”我们现在就来算。
	首先,我们已经知道,只需要把左边的查找一次,而右边的不需要查找。
并且,他每查找一次就节省了右边相应的查找次数,同样的每一次查找都至少前进一次。
那么查找的总次数就是N。然后,外面的for循环也是N次的。在N次的循环里做一共做N次所以时间复杂度就是O(N)
	如果你非要认为是O(N^2)就请从头再看一次博客吧。我也帮不了你了。
 
	现在也没什么好讲的了,manachar就是这么简单,不复来打我吖~
	既然没什么讲的了,那我还是说两句话吧
	真的就两句。
	。。。
	题目+题解。
 

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std ;
int p[220005];
int main()
{
	char str[110005],now[110005];
	while ((scanf("%s",str))!=EOF)
	{
		int size=0;
		size=strlen(str);
		now[0]='!';
		now[1]='#';
		int x=1;
		for (int i=0;i<size;i++)
		{
			now[++x]=str[i];
			now[++x]='#';
		}
		memset(p,0,sizeof p);
		int bj=0;
		int id=0;
		size=x;
		for (int i=1;i<=size;i++)
		{
			if (bj>i) 
				if (p[2*id-i]<bj-i)
					p[i]=p[2*id-i];
				else p[i]=bj-i;
			else p[i]=1;
			while(now[i-p[i]]==now[i+p[i]]) p[i]++;
			if (i+p[i]>bj)
			{
				bj=i+p[i];
				id=i;
			}
		}
		int max_=0,ii;
		for (int i=1;i<size;i++)
			if (p[i]>max_)
			{
				max_=p[i];
				ii=i;
			}
		max_--;
		int s=ii-max_;
		int e=ii+max_;
		int ans=0;
		for (int i=s;i<=e;i++)
			if (now[i]!='#') ans++;
		printf("%d\n",ans);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值