class Solution {
public:
int maxProfit(vector<int>& prices) {
int maxn = 0;
for(int i=0;i<prices.size()-1;i++){
if(prices[i]<prices[i+1]) maxn += prices[i+1]-prices[i];
}
return maxn;
}
};
在思路正确的情况下,最后一个用例一直过不去,超时,然后就想到问问chatgpt,最后他帮我优化了代码的效率,主要卡在两个问题上:
①substr()的效率很低,循环使用时间复杂度很高,我原先是循环使用substr在输出前删除前导0,比较傻的做法。
while(answer.size()>0 && answer[0] == '0'){
answer = answer.substr(1,answer.size()-1);
}
②原先出栈转字符串的操作也是类似用字符串循环拼接的思路,说明字符串拼接或者剪切本身复杂度是比较高的,最好是不要循环使用
//出栈转化结果
string answer = "";
while (ans.size() > 0) {
answer = ans.top() + answer;
ans.pop();
}
最后是修改后正确通过的代码
class Solution {
public:
string removeKdigits(string num, int k) {
if (num.size() == k) return "0";
int m = k;
stack<char> ans;
int i;
for (i = 0; i < num.size() && m != 0; i++) {
while (m > 0 && ans.size() > 0 && ans.top() > num[i]){
ans.pop();
m--;
}
if (num[i] != '0' || ans.size() > 0) {
ans.push(num[i]);
}
}
while(i<num.size()) ans.push(num[i++]);
while((m--)>0 && ans.size()>0) ans.pop();
//出栈转化结果
string answer(ans.size(), ' ');
int j = ans.size() - 1;
while (!ans.empty()) {
answer[j--] = ans.top();
ans.pop();
}
//再次删除前导0
int index = 0;
while (index < answer.size() && answer[index] == '0') {
index++;
}
if(answer.size() == 0) return "0";
return answer.substr(index == answer.size() ? index - 1 : index, answer.size());
}
};
int flag[30100];
class Solution {
public:
bool canJump(vector<int>& nums) {
if(nums.size() == 1) return true;
if(nums.size() == 0) return false;
for(int i=1;i<nums.size();i++){
flag[i] = 0;
}
flag[0] = 1;
for(int i=0;i<nums.size()-1;i++){
if(flag[i] == 1){
for(int j=1;j<=nums[i];j++){
if(i+j == nums.size()-1) return true;
flag[i+j] = 1;
}
}
}
return false;
}
};
flag[]
里面,除了第一个元素是0,他本身可以0步到达自己,其他都是int的最大值,用于后面的min
函数比较,每一次循环就将当前位置能达到的所有下标的值进行一个比较,如果当前位置值+1,比原先记录到达当前下标需要的最小步数小,则更新。int flag [10001];
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 0) return 0;
for(int i=1;i<nums.size();i++){
flag[i] = INT_MAX;
}
flag[0] = 0;
for(int i=0;i<nums.size()-1;i++){
for(int j=1;j<=nums[i] && i+j<nums.size();j++){
flag[i+j] = min(flag[i] + 1,flag[i+j]);
}
}
return flag[nums.size()-1];
}
};
i~j
中间都是无法作为起点的,对i~j
中间任意一点k
,若以k
作为起点,那么出发时的汽油储量只有gas[k]
,而从k之前的点i走过来,汽油储量应该>=gas[k]
(能够顺利走到k点,到的时候汽油起码是0),所以下一层循环可以直接从j+1开始考虑作为起点。class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int gasCount;
int n = gas.size();
for(int i=0; i<n; i++){
int j = i;
gasCount = gas[i];
while (gasCount - cost[j] >= 0) {
if (gasCount + gas[(j+1)%n] - cost[j] >= 0) {
gasCount = gasCount + gas[(j+1)%n] - cost[j];
j = (j + 1) % n;
if(j==i) return i;
}
}
if(j < i) return -1;
i = j;
}
return -1;
}
};
以下代码就是暴力的思路,每次查找失败都是i++
,导致时间复杂度过大,最后超时,有五个样例通不过,但是思路上是比较朴素能够想到的,上面那个正确的就可以基于这个思路得到。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int i = 0;
int j = 0;
int gasCount = 0;
int n = gas.size();
while (i < n) {
gasCount += gas[j];
if (gasCount - cost[j % n] >= 0) {
gasCount -= cost[j % n];
j = (j + 1) % n;
if(j==i) return i;
}
else {
//重置起点
i++;
j = i;
gasCount = 0;
}
}
return -1;
}
};
int数组
或者map
记录一下字符串里面各个字母出现的次数,在循环的过程中,检查当前扫描到的字母出现的次数,如果和最大次数相同,说明当前段已经包含了这个字母的所有取值,kinds
用于记录当前段中,出现次数已经达到最大的字母的数量,如果和map
的大小相等,说明当前段所有字母都取到了最大数,则返回当前段长,并清空记录信息,继续向前循环。class Solution {
public:
vector<int> partitionLabels(string s) {
int count[26];
map<char,int> nowKinds;
int kinds = 0;
int length = 0;
vector<int> ans;
int n = s.size();
for(int i=0;i<26;i++){
count[i] = 0;
}
//记录各字母出现频次
for(int i=0;i<n;i++) count[s[i]-'a']++;
for(int i=0;i<n;i++){
nowKinds[s[i]]++;
if(nowKinds[s[i]] == count[s[i]-'a']) kinds++;
length++;
if(nowKinds.size() == kinds){
ans.push_back(length);
length = 0;
nowKinds.clear();
kinds = 0;
}
}
return ans;
}
};
终于是自己的思路写了大差不差,就是统计字母频次之后,依次遍历数组入栈,如果栈顶元素比当前元素大,并且之后还会出现,就应该出栈,尽量保持字符串是一个递增的序列,这样可以尽可能让字典序大。
但就是最后还是有一个点问题导致几个样例过不去,就比如在"abacb"
中,只考虑以上思路会出现错误,原因就是当第二个‘a’
进入循环时,将前面的“ab”
都出战了,实际上,由于这里的ab已经保持了递增的序列,是不应该让让前面出栈的,正确的做法是忽略当前这个a
class Solution {
public:
string removeDuplicateLetters(string s) {
map<char,int> letter;
int n = s.size();
//字母频次记录
for(int i = 0; i < n; i++){
letter[s[i]] ++;
}
stack<char> ans;//用于接受字符的栈
map<char,int> now;//循环遍历字母,用一个新的map记录当前扫描到的字母频次
for(int i=0;i<26;i++){
f[i] = 0;
}
int f[26];//f用于标记当前字符是否已经存在于栈
for(int i=0;i<n;i++){
now[s[i]]++;
if(!f[s[i]-'a']){
while(ans.size()>0 && ans.top()>=s[i] && now[ans.top()]<letter[ans.top()]){
f[ans.top()-'a'] = 0;
ans.pop();
}
}
if(f[s[i]-'a'] == 0){
f[s[i]-'a']++;
ans.push(s[i]);
}
}
cout << ans.size() << endl;
string answer(letter.size(),' ');
//答案转化出栈
for(int i=answer.size()-1;i>=0;i--){
answer[i] = ans.top();
ans.pop();
}
return answer;
}
};
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if(intervals.empty()) return 0;
int n = intervals.size();
sort(intervals.begin(),intervals.end(),compare);
//记录上一个区间的右边界,最初始的为第一组数
int pre = 0;
int length = 0;
for(int i=1; i<n; i++){
if(intervals[i][0] >= intervals[pre][1]){
pre = i;
}else{
length++;
}
}
return length;
}
static bool compare(vector<int> &a, vector<int> &b){
return a[1] < b[1];
}
};
箭数 = 区间总数-重叠次数
,记住一个问题就是如果区间头尾相接,也算是能够一支箭射中两个。class Solution {
public:
//重叠区间的问题,相当于选最少的点覆盖所有区间
int findMinArrowShots(vector<vector<int>>& points) {
if(points.empty()) return 0;
int n = points.size();
sort(points.begin(),points.end(),compare);
//记录上一个区间的右边界,最初始的为第一组数
int pre = 0;
int count = 0;
for(int i=1; i<n; i++){
if(points[i][0] > points[pre][1]){
pre = i;
}else{
count++;
}
}
return n - count;
}
static bool compare(vector<int> &a, vector<int> &b){
return a[1] < b[1];
}
};