题目就不说了,免得侵权 (虽然好像是搬的)
个人认为是一道非常有意思的思维题
正解是 O ( ∑ i = 0 log n n 2 i ) = O ( n ) \mathcal O(\sum\limits_{i=0}^{\log n}\frac{n}{2^i})= \mathcal O(n) O(i=0∑logn2in)=O(n),这里讲一个 O ( n log log n ) \mathcal O(n \log \log n) O(nloglogn) 的方法
首先,本题答案一定 ≤ log n + 1 \le \log n+1 ≤logn+1
因为给出的串中最多只能找到 n n n 个不同的长度为 ( log n ) + 1 (\log n)+1 (logn)+1 的串。又因为 n < 2 ( log n ) + 1 n<2^{(\log n)+1} n<2(logn)+1,所以 ( log n ) + 1 (\log n)+1 (logn)+1 一定是可行方案。
那么,我们就只需要在 ≤ log n \le \log n ≤logn 的范围内寻找答案就可以了
然后,我们还可以发现答案具有可二分性
假设已知一个在原串中没有出现的,长度为 x x x 的串。那么,只要在该串的开头或末尾加上一个 0 / 1 0/1 0/1,就构造出了一个长度为 x + 1 x+1 x+1 的满足条件的串,同理可一直往后推,从而证明了答案的单调性
所以,可以直接在 log n \log n logn 的范围内二分 + O ( n ) \mathcal O(n) O(n) check。最后求出答案长度后,可以用线性时间求出字典序最小的答案串。总时间复杂度 O ( n log log n ) \mathcal O(n \log \log n) O(nloglogn)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int Maxn=16777216+20;
char s[Maxn];
bool a[Maxn];
int c[Maxn];
int n,m,cnt;
bool check(int len)
{
int cur=0,ret=0;
for(int i=1;i<=len;++i)
cur=(cur<<1)|a[i];
++c[cur];
if(c[cur]==1)++ret;
for(int i=len+1;i<=n;++i)
{
cur^=(a[i-len]<<(len-1));
cur=(cur<<1)|a[i];
++c[cur];
if(c[cur]==1)++ret;
}
fill(c,c+1+(1<<len),0);
if(ret==(1<<len))return 0;
return 1;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i)
if(s[i]=='1')a[i]=1;
if(n==1)
{
if(a[1])puts("0");
else puts("1");
return 0;
}
m=log2(n)+1;
int l=1,r=m;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
int len=l;
int cur=0;
for(int i=1;i<=len;++i)
cur=(cur<<1)|a[i];
c[cur]|=1;
for(int i=len+1;i<=n;++i)
{
cur^=(a[i-len]<<(len-1));
cur=(cur<<1)|a[i];
c[cur]|=1;
}
for(int x=0;x<(1<<len);++x)
if(!c[x])
{
for(int i=len-1;i>=0;--i)
if((x>>i) & 1)putchar('1');
else putchar('0');
putchar('\n');
return 0;
}
return 0;
}