题目链接:
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;
}