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

本文详细解析了GDOI2020模拟赛中的小B的环问题,通过字符串倍长和哈希技巧,讨论了如何在删除特定长度的子串后,使剩余字符串的首尾字符不相同。提供了完整的算法思路和C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值