给定一个字符串 s,计算具有相同数量0和1的非空(连续)子字符串的数量,并且这些子字符串中的所有0和所有1都是组合在一起的。
重复出现的子串要计算它们出现的次数。
示例 1 :
输入: "00110011"
输出: 6
解释: 有6个子串具有相同数量的连续1和0:“0011”,“01”,“1100”,“10”,“0011” 和 “01”。
请注意,一些重复出现的子串要计算它们出现的次数。
另外,“00110011”不是有效的子串,因为所有的0(和1)没有组合在一起。
示例 2 :
输入: "10101"
输出: 4
解释: 有4个子串:“10”,“01”,“10”,“01”,它们具有相同数量的连续1和0。
注意:
s.length 在1到50,000之间。
s 只包含“0”或“1”字符。
class Solution {
public:
int countBinarySubstrings(string s) {
if (s.size() == 0) return 0;
vector<int> vec_cnt;
int cnt = 1;
for(int i = 1; i < s.size(); ++i) {
if (s[i] == s[i-1]) {
++cnt;
} else {
vec_cnt.push_back(cnt);
cnt = 1;
}
}
vec_cnt.push_back(cnt);
cnt = 0;
for(int i = 1; i < vec_cnt.size(); ++i) {
cnt += min(vec_cnt[i], vec_cnt[i-1]);
}
return cnt;
}
};
class Solution {
public:
int countBinarySubstrings(string s) {
if (s.size() == 0) return 0;
int cnt_pre = 0, cnt_cur = 1, cnt_res = 0;
for (int i = 1; i < s.size(); ++i) {
if (s[i] == s[i-1]) {
++cnt_cur;
} else {
cnt_res += min(cnt_pre, cnt_cur);
cnt_pre = cnt_cur;
cnt_cur = 1;
}
}
cnt_res += min(cnt_pre, cnt_cur);
return cnt_res;
}
};
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
示例 2:
输入:
gas = [2,3,4]
cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
//优先队列好像毫无意义,耗时耗空间,还不如直接遍历检查每个符合gas[i] - cost[i] >= 0的点开始循环检查
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
if (gas.size() == 0) return -1;
auto cmp = [&gas, &cost](int &i, int &j) { //
return gas[i] - cost[i] < gas[j] - cost[j];
};
priority_queue<int, vector<int>, decltype(cmp)> que_pos(cmp); // pos with max(gas[i] - cost[i])
for (int i = 0; i < gas.size(); ++i) {
que_pos.push(i);
}
while (que_pos.size()) {
auto cur_pos = que_pos.top();
if (gas[cur_pos] - cost[cur_pos] < 0) return -1;
que_pos.pop();
int sum_diff = 0;
for (int i = 0; i < gas.size(); ++i) {
auto tmp_pos = (cur_pos + i) % gas.size();
sum_diff += gas[tmp_pos] - cost[tmp_pos];
if (sum_diff < 0) break;
}
if (sum_diff >= 0) return cur_pos;
}
return -1;
}
};
class Solution { // 如果A站不能到B站,那么A,B之间到任何一个站都不能到B站,(B站是A站第一个不能到的站)
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
if (gas.size() == 0) return -1;
int pos_A = 0, sum_diff = 0, all_sum_diff = 0;
for (int i = 0; i < gas.size(); ++i) {
all_sum_diff += gas[i] - cost[i];
sum_diff += gas[i] - cost[i];
if (sum_diff < 0) {
pos_A = i + 1;
sum_diff = 0;
continue;
}
}
return all_sum_diff >= 0 ? pos_A : -1;
}
};
对链表进行插入排序。
插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。
每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。
插入排序算法:
插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
重复直到所有输入数据插入完为止。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
ListNode *LN = new ListNode(0);
auto cur = head;
while (cur) {
auto tmp = LN->next, pre = LN;
while (tmp && tmp->val <= cur->val) {
pre = tmp;
tmp = tmp->next;
}
pre->next = cur;
cur = cur->next; // 顺序问题
pre->next->next = tmp; //
}
cur = LN->next;
delete LN;
return cur;
}
};
给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
class Solution {
public:
void reorderList(ListNode* head) {
if (!head || !head->next) return ;
auto slow = head, fast = head->next;
while (fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
auto cur = slow->next;
slow->next = nullptr;
ListNode *pre = nullptr, *next;
while (cur) {
next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
auto first = head, second = pre;
while (second) {
next = first->next;
first->next = second;
second = second->next;
first->next->next = next;
first = next;
}
}
};
根据 逆波兰表示法,求表达式的值。
有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入: ["2", "1", "+", "3", "*"]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入: ["4", "13", "5", "/", "+"]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
输出: 22
解释:
该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> sta;
for (auto &t: tokens) {
if (t == "+" || t == "-" || t == "*" || t == "/") { // t == '+' 报错string和char不能比较
int le, ri;
ri = sta.top(); // le 和 ri顺序易错点
sta.pop();
le = sta.top(); // le 和 ri顺序易错点
sta.pop();
int pop_val;
if (t == "+") // switch... case "+" 依旧报错
pop_val = le + ri;
else if (t == "-")
pop_val = le - ri;
else if (t == "*")
pop_val = le * ri;
else
pop_val = le / ri;
sta.push(pop_val);
} else {
sta.push(stoi(t));
}
}
return sta.top();
}
};