【CF1469E】A Bit Similar

题目

题目链接:https://codeforces.com/problemset/problem/1469/E
定义两个长度均为 (k) 的 01 串 (s,t) 是相似的,当且仅当存在一个 (iin[1,k]),使得 (s_i=t_i)
给定一个长度为 (n) 的 01 串 (s),求一个长度为 (k) 的 01 串 (t),使得 (t)(s) 所有长度为 (k) 的子串都是相似的。如果有多个解,输出字典序最小的。
多测。
(sum nleq 10^6)(kleq 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 imes 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
"; return; }
	cout<<"YES
";
	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<<"
";
}

int main()
{
	memset(v,-1,sizeof(v));
	scanf("%d",&T); tot=T+1;
	while (T--) Work();
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/15250480.html