所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。
编写一个函数来查找目标子串,目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次。
示例:
输入:s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
输出:["AAAAACCCCC", "CCCCCAAAAA"]
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
if (s.size() <= 10) return {};
unordered_map<char, int> char_to_val = {
{'A', 0}, {'C', 1}, {'G', 2}, {'T', 3} // 1 2 3 4 不正确
};
int mod = pow(4, 10 - 1);
int val = 0;
unordered_map<int, int> reval;
vector<string> res;
for (int i = 0; i < s.size(); ++i) {
if (i >= 10) {
val = ((val % mod) << 2) + char_to_val[s[i]];
if (++reval[val] == 2) { // 只保存第二次重复出现的字符串,二次以上不需要计数
res.push_back(s.substr(i - 9, 10));
}
} else {
val = (val << 2) + char_to_val[s[i]];
if (i == 9) {
++reval[val];
}
}
}
return res;
}
};
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: "3+2*2"
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。
class Solution { // 测试用例处理不了 "1+1 2"的情况,所以不考虑,但得考虑连续空格的情况" 1 + 2 "
public:
int calculate(string s) {
deque<int> sta_int;
deque<char> sta_char;
for (int i = 0, n = 0; i <= s.size(); ++i) {
if (i == s.size() || s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
if (n == 0) continue;
sta_int.push_back(stoi(s.substr(i - n, n)));
while (sta_char.size() && (sta_char.back() == '*' || sta_char.back() == '/')) { // 计算 * /
auto b = sta_int.back();
sta_int.pop_back();
auto a = sta_int.back();
sta_int.pop_back();
sta_int.push_back(helper(a, b, sta_char.back())); // a b出双端队列头的顺序
sta_char.pop_back();
}
if (i == s.size()) {
break;
}
sta_char.push_back(s[i]);
n = 0;
} else {
++n;
}
}
while (sta_char.size()) { // 计算 + -
auto a = sta_int.front();
sta_int.pop_front();
auto b = sta_int.front();
sta_int.pop_front();
sta_int.push_front(helper(a, b, sta_char.front())); // a b出双端队列头的顺序
sta_char.pop_front();
}
return sta_int.back();
}
int helper(const int& a, const int& b, const char& c) {
switch (c) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return INT_MAX;
}
};
给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。
若可行,输出任意可行的结果。若不可行,返回空字符串。
示例 1:
输入: S = "aab"
输出: "aba"
示例 2:
输入: S = "aaab"
输出: ""
注意:
S 只包含小写字母并且长度在[1, 500]区间内。
前言
这道题是典型的贪心算法的题。重构字符串时,需要根据每个字母在字符串中出现的次数处理每个字母放置的位置。如果出现次数最多的字母可以在重新排布之后不相邻,则可以重新排布字母使得相邻的字母都不相同。如果出现次数最多的字母过多,则无法重新排布字母使得相邻的字母都不相同。
假设字符串的长度为 nnn,如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现多少次?
当 nnn 是偶数时,有 n/2n/2n/2 个偶数下标和 n/2n/2n/2 个奇数下标,因此每个字母的出现次数都不能超过 n/2n/2n/2 次,否则出现次数最多的字母一定会出现相邻。
当 nnn 是奇数时,由于共有 (n+1)/2(n+1)/2(n+1)/2 个偶数下标,因此每个字母的出现次数都不能超过 (n+1)/2(n+1)/2(n+1)/2 次,否则出现次数最多的字母一定会出现相邻。
由于当 nnn 是偶数时,在整数除法下满足 n/2n/2n/2 和 (n+1)/2(n+1)/2(n+1)/2 相等,因此可以合并 nnn 是偶数与 nnn 是奇数的情况:如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现 (n+1)/2(n+1)/2(n+1)/2 次。
因此首先遍历字符串并统计每个字母的出现次数,如果存在一个字母的出现次数大于 (n+1)/2(n+1)/2(n+1)/2,则无法重新排布字母使得相邻的字母都不相同,返回空字符串。如果所有字母的出现次数都不超过 (n+1)/2(n+1)/2(n+1)/2,则考虑如何重新排布字母。
以下提供两种使用贪心算法的方法,分别基于最大堆和计数。
方法一:基于最大堆的贪心算法
维护最大堆存储字母,堆顶元素为出现次数最多的字母。首先统计每个字母的出现次数,然后将出现次数大于 000 的字母加入最大堆。
当最大堆的元素个数大于 111 时,每次从最大堆取出两个字母,拼接到重构的字符串,然后将两个字母的出现次数分别减 111,并将剩余出现次数大于 000 的字母重新加入最大堆。由于最大堆中的元素都是不同的,因此取出的两个字母一定也是不同的,将两个不同的字母拼接到重构的字符串,可以确保相邻的字母都不相同。
如果最大堆变成空,则已经完成字符串的重构。如果最大堆剩下 111 个元素,则取出最后一个字母,拼接到重构的字符串。
对于长度为 nnn 的字符串,共有 n/2n/2n/2 次每次从最大堆取出两个字母的操作,当 nnn 是奇数时,还有一次从最大堆取出一个字母的操作,因此重构的字符串的长度一定是 nnn。
当 nnn 是奇数时,是否可能出现重构的字符串的最后两个字母相同的情况?如果最后一个字母在整个字符串中至少出现了 222 次,则在最后一次从最大堆取出两个字母时,该字母会先被选出,因此不会成为重构的字符串的倒数第二个字母,也不可能出现重构的字符串最后两个字母相同的情况。
因此,在重构字符串可行的情况下,基于最大堆的贪心算法可以确保得到正确答案。
class Solution {
public:
string reorganizeString(string S) {
vector<int> vec(26, 0);
int max_cnt = 0;
string res = "";
for (auto &s: S) {
++vec[s - 'a'];
max_cnt = max(max_cnt, vec[s - 'a']);
}
if (max_cnt > (S.size() + 1)/2)
return "";
auto cmp = [&vec](const int& a, const int& b) { //
return vec[a] < vec[b];
};
priority_queue<int, vector<int>, decltype(cmp)> p_que{cmp}; //
for (int i = 0; i < 26; ++i)
if (vec[i])
p_que.push(i);
while (p_que.size() > 1) {
auto a = p_que.top();
p_que.pop();
auto b = p_que.top();
p_que.pop();
res = res + static_cast<char>(a + 'a') + static_cast<char>(b + 'a'); // res += 出错
if (--vec[a]) p_que.push(a);
if (--vec[b]) p_que.push(b);
}
if (p_que.size())
res += static_cast<char>(p_que.top() + 'a');
return res;
}
};