【CF1469E】A Bit Similar

题目

题目链接:https://codeforces.com/problemset/problem/1469/E
定义两个长度均为 \(k\) 的 01 串 \(s,t\) 是相似的,当且仅当存在一个 \(i\in[1,k]\),使得 \(s_i=t_i\)
给定一个长度为 \(n\) 的 01 串 \(s\),求一个长度为 \(k\) 的 01 串 \(t\),使得 \(t\)\(s\) 所有长度为 \(k\) 的子串都是相似的。如果有多个解,输出字典序最小的。
多测。
\(\sum n\leq 10^6\)\(k\leq n\)

思路

等价于把 \(s\) 按位取反,然后求一个字典序最小的长度为 \(k\) 的 01 串 \(t\),使得 \(t\) 不为 \(s\) 的子串。
因为要字典序最小,那么从前往后依次考虑每一位能不能取 \(0\)。假设已经确定了前 \(i-1\) 位,那么第 \(i\) 位能取 \(0\) 当且仅当:设 01 串 \(t'=\)\(i-1\) 位的 01 串 + 0,对于 \(t'\)\(s\) 所有位置出现的子串,其后面 \(k-i\) 位,所形成的 01 串集合,大小小于 \(2^{k-i}\)。否则如果这一位选 \(0\),后面无论怎么选都肯定是 \(s\) 的子串了。
\(s\) 长度为 \(k\) 的子串数量有 \(n-k+1\) 个,也就是当 \(i>\log n\) 的时候,这一位一定可以选 \(0\)
那么只需要对最后 \(20\) 位分别处理即可。时间复杂度 \(O(n\times \max(k,\log n))\)

代码

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

const int N=(1<<20)+50;
int n,m,T,a[N],c[N],b[N];
ll tot,v[N];

bool check()
{
	if (m>20) return 1;
	int cnt=0;
	for (int i=1;i<=n-m+1;i++)
		if (v[b[i]]!=T) v[b[i]]=T,cnt++;
	if (cnt==(1<<m)) return 0;
	return 1;
}

void Work()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%1d",&a[i]);
		c[i]=c[i-1]+a[i]; b[i]=0;
	}
	for (int i=1;i<=n;i++)
		for (int j=1;j<=min(m,20);j++)
			b[i]=(b[i]<<1)|a[i+j-1];
	if (!check()) { cout<<"NO\n"; return; }
	cout<<"YES\n";
	for (int i=21;i<=m;i++) putchar(48);
	int k=max(0,m-20),s=0;
	m=min(m,20);
	for (int i=m;i>=1;i--)
	{
		int cnt=0; tot++;
		for (int j=k+1;j<=n-m+1;j++)
		{
			if (c[j-1]-c[j-k-1]!=k) continue;
			if ((b[j]>>(i-1))!=(s<<1)+1) continue;
			int val=(b[j]&((1<<i-1)-1));
			if (v[val]!=tot) v[val]=tot,cnt++;
		}
		if (cnt==(1<<i-1)) s=(s<<1),putchar(49);
			else s=(s<<1)|1,putchar(48); 
	}
	cout<<"\n";
}

int main()
{
	memset(v,-1,sizeof(v));
	scanf("%d",&T); tot=T+1;
	while (T--) Work();
	return 0;
}
posted @ 2021-09-10 11:03  stoorz  阅读(49)  评论(0编辑  收藏  举报