JZOJ 6470. 【GDOI2020模拟02.13】小 B 的环
题解
- 简化一下题目就是问删去一段子串或一段前缀+后缀,且删去总长度为
k
k
k(
k
∈
[
0
,
n
)
kin[0,n)
k∈[0,n))能不能使剩下部分首尾相接后相邻字符不同,
- 方便起见,可以把字符串倍长,这样只通过删去前缀+后缀可以达到一样的目的。
- 题目是要删去,不妨换种思路,改成保留
l
l
l个长度(
l
∈
[
1
,
n
]
lin[1,n]
l∈[1,n]),这样这
l
l
l个字符一定要求连续,
- 首先把每处相同字符相邻处断开,将一个串分成若干段,因为他们连起来不可能构成答案,所以只需分开在每个单独的串内看,
- 这样就可以保证相邻的不同了,但不能保证首尾一定不同,
- 举个例子,在长度为
l
e
n
=
6
len=6
len=6的一段(这是已经分开后的一段)中要取出
l
=
4
l=4
l=4的一串,有3种情况,
- 分别是
[
1
,
4
]
[1,4]
[1,4],
[
2
,
5
]
[2,5]
[2,5],
[
3
,
6
]
[3,6]
[3,6],那么就要分别看
s
t
[
1
]
st[1]
st[1]和
s
t
[
4
]
st[4]
st[4]是否相同,
s
t
[
2
]
st[2]
st[2]和
s
t
[
5
]
st[5]
st[5]是否相同,
s
t
[
3
]
st[3]
st[3]和
s
t
[
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
",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("
");
}
return 0;
}