[NOI2014]动物园 题解

  • Post author:
  • Post category:其他




本题的hash解法

作者由于只有CSPJ水平,所以根本想不出KMP或者什么自动机解法。只会hash+二分

虽然该算法复杂度为O(Tnlogn),但是常数小,可以通过。。


考虑j>i,如果i~j这个字符串同时是串1-j的前缀和后缀,那么我们可以认为位置i对num[j]做出了1的贡献。

然后我们枚举i,看它可以对哪些num[j]产生贡献。容易发现如果k<j且i对num[j]产生了贡献,那么i也一定会对num[k]产生贡献。因此我们可以二分出一个x,使得i对num[i]->num[k]产生贡献,且不会对num[k+1]->num[n]产生贡献。

然后考虑二分过程中如何checki是否对当前的mid(我设的mid是j-i+1的值即前/后缀的长度)产生贡献:用hash判断一下字符1->mid组成的字符串和字符x->x+mid-1组成的字符串是否相同就可以了。然后统计答案就用差分数组乱搞一下就好了。

枚举i是o(n)的,二分是o(logn)的,所以总复杂度为o(Tnlogn),因为常数小可以通过。


上文中的->可以理解为~,因为作者对markdown太不了解所以只能这么写了。

排版很丑,表述很垃圾,代码也丑,希望轻喷。


code:

#include<bits/stdc++.h> 
using namespace std;
const int N=1e6+5,mod=1e9+7,md=19260817;char c[N];int hash[N],n,pre[N],pows[N];
inline void work(int x){
	if(c[x]!=c[1])return ;int l=1,r=min(x-1,n-x+1);
	while(l<r){int mid=l+r+1>>1; 
		if((hash[x+mid-1]-(long long)hash[x-1]*(long long)pows[mid]%md+md)%md==hash[mid])l=mid;else r=mid-1;
	}++pre[x];--pre[x+l];
}
signed main(){
	int t;scanf("%d",&t);while(t--){
		scanf("%s",c+1);n=strlen(c+1);pows[0]=1;memset(pre,0,sizeof(pre)); 
		for(int i=1;i<=n;++i)hash[i]=(hash[i-1]*26+c[i]-'a')%md,pows[i]=pows[i-1]*26%md;
		for(int i=2;i<=n;++i)work(i);for(int i=1;i<=n;++i)pre[i]+=pre[i-1];
		long long ans=1;for(int i=2;i<=n;++i)ans=ans*((long long)pre[i]+1ll)%mod;
		printf("%lld\n",ans);
	}return 0; 
} 

代码中的hash就是hash值,pow[i]是26的i次方。然后pre是num的差分数组。个人认为代码很好理解。



版权声明:本文为avenLJY原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。