力扣每日一题 3211. 生成不含相邻零的二进制字符串
给你一个正整数 n
。
如果一个二进制字符串 x
的所有长度为 2 的子字符串中包含 至少 一个 "1"
,则称 x
是一个 有效 字符串。
返回所有长度为 n
的 有效 字符串,可以以任意顺序排列。
只要看懂了题目意思,这个题目就无脑暴力就可以了。
第一种方法:用递归,只要考虑根据前面填的数字,后面可以填哪些数字就可以了。
题目要求的就是二进制串没有连续的0出现。
我们可以直接枚举字符串的位置 i 填 1 还是 0:
如果前一个位置是0,那这个位置自然只能是1.
如果前一个位置是1,那这个位置不管是0还是1都可以。
枚举从i=1开始,到 i=n 结束把字符串填入数组即可。
class Solution {
public:
void dfs(string s, int len)
{
if (len == 0)
{
ans.push_back(s);
return;
}
if (s[s.length()-1]=='0') dfs(s+"1", len-1);
else
{
dfs(s+"0", len-1);
dfs(s+"1", len-1);
}
}
vector<string> validStrings(int n) {
ans.clear();
str="";
nlen = n;
dfs("0", n-1);
dfs("1", n-1);
return ans;
}
private:
string str;
vector<string> ans;
int nlen;
};
第二种方法:直接枚举所有二进制长度为n的整数,判断是否满足条件,满足就把数字的二进制填进数组即可。
最容易想到的检查方法就是写出它的二进制,然后遍历找有没有连续的0.
不过其实还有种更快的方法检查,那就是位运算。怎么算呢?我们知道 x & (x >> 1) 的结果可以判断x是否有连续的1,那么我们只要稍微改一下,把x取反,就可以判断有没有连续的0了。
那怎么取反呢?
创建一个低 n 位全为 1 的二进制数 mask = (1 << n) - 1。
计算 x ^ mask,由于 0 和 1 异或后变成了 1,1 和 1 异或后变成了 0,所以 x 的低 n 位都取反了。
class Solution {
public:
vector<string> validStrings(int n) {
vector<string> ans;
int mask = (1 << n) - 1;
for (int i = 0; i < (1 << n); i++)
{
int x = mask ^ i;
if (!(x >> 1 & x))
{
ans.push_back(bitset<18>(x^mask).to_string().substr(18 - n));
}
}
return ans;
}
private:
string str;
vector<string> ans;
int nlen;
};