codeforces 1469 E. A Bit Similar(unorderedmap + bitset)

6 篇文章 0 订阅
3 篇文章 0 订阅

题目链接:

https://codeforces.com/contest/1469/problem/E

题意:

给你一个长度为n的01串,请问你能否构造出一个k长度的01串使这个构造出来的串与原串的所有k长度的子串都至少有1位相同(同位数字符一样) NO/YES输出构造串

n,k <= 1e6

思路:

算是个思维题⑧

2^20 > 1e6(n - k + 1种子串的情况) 所以对于超出20位的部分可以一律不考虑 

将长度为k的串分成两部分 前Len(=n - K) + 后K(=min(k,20))

前Len均置为0以便字典序最小 即反码均为1 我们最终找到一个答案(构造串)的反码 使这个反码与任何原串子串都不完全一致即可

所以构造串反码后半段的最优解是全1 此时如果符合满足就最好了 不行就按字典序顺序依次找直到找到答案或没有可行解。

unorderedmap<bitset<20>,bool>mp的骚操作太妙了简直!

bitset<20>qaq;

qaq.any();//检查qaq里收存在1

qaq.test(i);//检查qaq的第i位是不是1

qaq.set(i);//第i位置1 reset置0

代码: 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<bitset>
#include<unordered_map>

using namespace std;
const int MaxN = 1e6 + 5;

int k,t,n,K,Len;
char s[MaxN];
int d[MaxN];
unordered_map<bitset<20>,bool>mp;

int main()
{
	scanf("%d",&t);
	while(t--){
		bool ok = 0;
		int pre = 0;
		mp.clear();
		bitset<20>qaq;
		scanf("%d %d",&n,&k);
		scanf("%s",s + 1);
		for(int i = 1;i <= n; i++) d[i] = s[i] - 48;
		K = min(k,20);
		Len = k - K;
		for(int i = 1;i <= Len; i++){
			pre += (d[i] == 0);
		}
		//前半段有几个0 前半段没有0就是我们的重点观察对象!!
		for(int i = k,j = 0;i >= k - K + 1; i--,j++){
			if(d[i] == 1) qaq.set(j);
		}//把后半段扒下来
		if(pre == 0) mp[qaq] = 1;
		//把这些重点观察的后半段记录下来
		for(int i = k + 1;i <= n; i++){
			if(!d[i - k]) pre--;//前面少一个
			if(!d[i - K]) pre++;//后面多一个
			qaq.reset(K - 1);//后半段最前面那位归0
			qaq <<= 1;//左移 将第0位空出来
			if(d[i] == 1) qaq.set(0);//重新填写第0位
			if(pre == 0) mp[qaq] = 1;//重点观察打标记
		}
		qaq.reset();//bitset的标记使命完成 下面构造答案
		for(int i = 0;i < K; i++) qaq.set(i);//后半段全置1 此时为最优解
		if(!mp.count(qaq)) ok = 1;//如果此解ok 直接输出答案完事儿
		while(qaq.any() && !ok){//后半段中存在1&&还没找到答案 没有1就是最坏结果了还不行就只能不行了
			for(int i = 0;i < K; i++){
				if(qaq.test(i)){
					qaq.reset(i);
					for(int j = 0;j < i; j++) qaq.set(j);
					break;
				}
			}//按顺序枚举所有情况
			if(!mp.count(qaq)) ok = 1;
		}
		if(!ok) printf("NO\n");
		else{
			printf("YES\n");
			for(int i = 1;i <= Len; i++) printf("0");
			for(int i = K - 1;i >= 0; i--){
				printf("%d",!qaq.test(i));
			}
			printf("\n");
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值