JZOJ 6470. 【GDOI2020模拟02.13】小 B 的环(字符串哈希)

JZOJ 6470. 【GDOI2020模拟02.13】小 B 的环

题解

  • 简化一下题目就是问删去一段子串或一段前缀+后缀,且删去总长度为 k k k [ 0 , n ) k\in[0,n) )能不能使剩下部分首尾相接后相邻字符不同,
  • 方便起见,可以把字符串倍长,这样只通过删去前缀+后缀可以达到一样的目的。
  • 题目是要删去,不妨换种思路,改成保留 l l 个长度( l [ 1 , n ] l\in[1,n] ),这样这 l l 个字符一定要求连续,
  • 首先把每处相同字符相邻处断开,将一个串分成若干段,因为他们连起来不可能构成答案,所以只需分开在每个单独的串内看,
  • 这样就可以保证相邻的不同了,但不能保证首尾一定不同,
  • 举个例子,在长度为 l e n = 6 len=6 的一段(这是已经分开后的一段)中要取出 l = 4 l=4 的一串,有3种情况,
  • 分别是 [ 1 , 4 ] [1,4] , [ 2 , 5 ] [2,5] , [ 3 , 6 ] [3,6] ,那么就要分别看 s t [ 1 ] st[1] s t [ 4 ] st[4] 是否相同, s t [ 2 ] st[2] s t [ 5 ] st[5] 是否相同, s t [ 3 ] st[3] s t [ 6 ] st[6] 是否相同,只要有一组不同那就是可行的。
  • 但这样浪费时间,反过来考虑,只有这每组全都不同才不可行,那可以直接判断两个字符串是否相同,也就是 [ 1 , 3 ] [1,3] [ 4 , 6 ] [4,6] 是否相同,这样对应的每组都可以判断到了,用字符串哈希即可。

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define N 5000010
#define ll long long
char st[N*2];
ll f[N*2],g[N*2];
int n,p[N];
ll ch(int l,int r)
{
   return (f[l]-f[r+1])*g[r];
}
int main()
{
   g[0]=1;
   for(int i=1;i<=10000000;i++) g[i]=g[i-1]*29;
   while(scanf("%s\n",st+1)!=EOF)
   {
   	n=strlen(st+1);
   	int i,j;
   	for(i=n+1;i<=2*n;i++) st[i]=st[i-n];
   	ll t=1;
   	f[2*n+1]=0;
   	for(i=2*n;i;i--) f[i]=(st[i]-'a'+1)*t+f[i+1],t*=29;
   	for(i=1;i<=n;i++) p[i]=0;
   	int l=1;
   	for(i=1;i<=2*n;i++) if(i==2*n||st[i]==st[i+1])
   	{
   		for(j=1;j<=n&&j<=i-l+1;j++)
   		{
   			if(ch(l,i-j+1)!=ch(l+j-1,i)) p[j]=1;
   		}
   		l=i+1;
   	}
   	for(i=n;i;i--) printf("%d",p[i]);
   	printf("\n");
   }
   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39565901/article/details/104328757