leetcode-1461. 检查一个字符串是否包含所有长度为 K 的二进制子串
给你一个二进制字符串 s 和一个整数 k 。
如果所有长度为 k 的二进制字符串都是 s 的子串,请返回 true ,否则请返回 false 。
示例 1:
输入:s = “00110110”, k = 2
输出:true
解释:长度为 2 的二进制串包括 “00”,“01”,“10” 和 “11”。
它们分别是 s 中下标为 0,1,3,2 开始的长度为 2 的子串。
本人最笨的办法
class Solution {
public:
bool hasAllCodes(string s, int k) {
int len = s.length();
if(len < k) return false;
unordered_set<string> us;
for(int i = 0; i <= len - k; i++) {
us.insert(s.substr(i, k));
}
// 快速幂计算 2^k
int ans = 1;
long long int cur = 2;
while(k) {
if(k&1) {
ans *= cur;
}
k >>= 1;
cur = cur*cur;
}
return us.size() == ans;
}
};
y^0b101 = 1y^0b100 * 0y^0b10 * 1y^0b1
= 1y^4 * 0y^2 * 1y^1
哈希
【leetcode的官方答案】
如果 s 包含 2^k 个长度为 k 的二进制串,那么它的长度至少为 2^k+k-1
class Solution {
public:
bool hasAllCodes(string s, int k) {
if (s.size() < (1 << k) + k - 1) {
return false;
}
unordered_set<string> exists;
for (int i = 0; i + k <= s.size(); ++i) {
exists.insert(move(s.substr(i, k))); // move语义不错
}
return exists.size() == (1 << k);
}
};
时间复杂度:O(k * L)
L 是字符串 s 的长度。
将长度为 k 的字符串加入哈希表的时间复杂度为 O(k),即为计算哈希值的时间。
空间复杂度:O(k * 2^k)
哈希表中最多有 2^k 项,每一项是一个长度为 k 的字符串。
哈希表 + 滑动窗口
class Solution {
public:
bool hasAllCodes(string s, int k) {
if (s.size() < (1 << k) + k - 1) {
return false;
}
int num = stoi(s.substr(0, k), nullptr, 2);
unordered_set<int> exists = {num};
for (int i = 1; i <= s.size() - k; ++i) {
// 加、减 的优先级高于 << 和 >>
// 所以一定要给 ((s[i - 1] - '0') << (k - 1)) 加括号
// num = num - ((s[i - 1] - '0') << (k - 1))) * 2 + (s[i + k - 1] - '0');
num = num * 2 - ((s[i - 1] - '0') << k) + (s[i + k - 1] - '0');
exists.insert(num);
}
return exists.size() == (1 << k);
}
};
时间复杂度:O(L)
空间复杂度:O(2^k)
哈希表中最多有 2^k 项,每一项是一个十进制整数。
value[s[0,k)] = s[0]*2^(k-1) + s[1]*2^(k-2) + … + s[k-1]*2^(0)
value[s[1,k+1)] = s[1]*2^(k-1) + s[2]*2^(k-2) + … + s[k]*2^(0)
如何从 value[s[0,k)] 推导出 value[s[1,k+1)] :
value[s[1,k+1)] = 2 * (s[1]*2^(k-2) + s[2]*2^(k-3) + … + s[k-1]*2^(0)) + s[k]*2^(0)
= 2 * (s[0]*2^(k-1) + s[1]*2^(k-2) + … + s[k-1]*2^(0) - s[0]*2^(k-1)) + s[k]*2^(0)
= 2 * value[s[0,k)] - s[0]*2^k + s[k]
所以:
当知道 value[s[i-1,k+i-1)] 后,value[s[i,k+i)] 的计算如下:
value[s[i,k+i)] = 2 * value[s[i-1,k+i-1)] - s[i-1]*2^k + s[k + i - 1]
num = num * 2 - s[i-1] * 2^k + s[k + i - 1]
2^k = 1 << k;
stoi 和 atoi 的学习
atoi()的参数是 const char* , 对于一个字符串str必须调用 c_str()把这个string转换成 const char类型,
stoi()的参数是 const string, 不需要转化为 const char*;
stoi()会做范围检查,默认范围在int的范围内,如果超出范围的话则会runtime error!
atoi()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界;
int stoi (const string& str, size_t* idx = 0, int base = 10);
Convert string to integer
Parses str interpreting its content as an integral number of the specified base, which is returned as an int value.
If idx is not a null pointer, the function also sets the value of idx to the position of the first character in str after the number.
The function uses strtol (or wcstol) to perform the conversion.
// stoi example
#include <iostream> // std::cout
#include <string> // std::string, std::stoi
int main ()
{
std::string str_dec = "2001, A Space Odyssey";
std::string str_hex = "40c3";
std::string str_bin = "-10010110001";
std::string str_auto = "0x7f";
std::string::size_type sz; // alias of size_t
int i_dec = std::stoi (str_dec,&sz); // sz 指向数字完之后的数字
int i_hex = std::stoi (str_hex,nullptr,16);
int i_bin = std::stoi (str_bin,nullptr,2);
int i_auto = std::stoi (str_auto,nullptr,0);
// str_dec.substr(sz) 指的是str_dec从sz开始到结尾的字符串
std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
std::cout << str_hex << ": " << i_hex << '\n';
std::cout << str_bin << ": " << i_bin << '\n';
std::cout << str_auto << ": " << i_auto << '\n';
return 0;
}
#include <iostream> // std::cout
#include <string> // std::string, std::stoi
int main ()
{
std::string str_dec = "2147483647abc";
std::string::size_type sz; // alias of size_t
int i_dec = std::stoi (str_dec,&sz); // sz 指向数字完之后的字符
// 2147483647 and [abc]
std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
return 0;
}
如果是 2147483647123 就runtime error了