leetcode刷题记录---19.9.28 颜色分类三指针,买卖股票模板dp

1.颜色分类

题目描述:0,1,2分别代表红色,白色,蓝色。给定一个数组是由这三种颜色组成

要求给这个数组排好序(红白蓝),不能使用库函数sort。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

思路:荷兰国旗问题

三个指针解决,一个指向最前面0的区域最左边,一个指向最后边2的区域的最左边,还有一个当前正在遍历的指针。

1.声明指针

2.只要当前指针不超过p2指针。

         3.如果当前指针指向的元素为0,交换p0,并++

         4.如果为2,交换p2,p2--就可以,当前指针不动。

         5.其他情况(指向1),当前指针右移。

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int p0 = 0;
        int cur = 0;
        int p2 = nums.size()-1;
        while(cur<=p2){
            if(nums[cur] == 0) swap(nums[p0++],nums[cur++]);
            else if(nums[cur] == 2) swap(nums[cur],nums[p2--]);
            else cur++;
        }
    }
};

2.除法求值                   (暂时放过)

题目描述:输入vector<pair<string,string>> equations,这是给定的方程式,vector<double>& values,这是前面方程式的计算结果。vector<pair<string,string>> queries,这是问题方程式。根据已经给定的方程式求解问题方程式,如果没有结果那就返回-1.0

示例 :
给定 a / b = 2.0, b / c = 3.0
问题: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 
返回 [6.0, 0.5, -1.0, 1.0, -1.0 ]

思路:并查集B站:[视频]小旭讲解 LeetCode 399. Evaluate Division 并查集,讲的并不好

dfs和bfs(1.build构造初始化图2.dfs或者bfs进行遍历)

 

3.最佳买卖股票时机含冷冻期          dp table框架

题目描述:给定一个数组,第i个元素就表示在第i天股票的价格。设计一个算法算出最大利润,你可以多次买卖一只股票,但是不能同时参加多笔交易,买之前出售掉之前的股票。卖出股票后无法在第二天买入股票。冷冻期为1天。

示例:

输入: [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

思路:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/solution/yi-ge-fang-fa-tuan-mie-6-dao-gu-piao-wen-ti-by-lab/

1种方法秒杀6到股票题。

根据上面的状态转移方程,秒杀题目;

第一题,k =1。直接套状态转移方程,根据base case,可做化简,因为k已经恒为1,所以k对状态转移已经没有影响。

dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])

dp[i][1] = max(dp[i-1][1],-prices[i])

针对本题:

每次sell要等一天才能继续交易,融入状态方程。

dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i])

dp[i][1] = max(dp[i-1][1],dp[i-2][0]-prices[i])

 

解题步骤:

1.定义base case :   int dp_i_0 = 0;int dp_i_1 = -price[0];int dp_p_0 = 0;

2.对每一天,写入状态转移方程

class Solution {
public:
	int maxProfit(vector<int>& prices) {
		int n = prices.size();
		if (0 == n) return 0;
		int dp_i_0 = 0;
		int dp_i_1 = -prices[0];
		int dp_p_0 = 0;
		int temp;
		for (int i = 0; i < n; i++)
		{
			temp = dp_i_0;
			dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
			dp_i_1 = max(dp_i_1, dp_p_0 - prices[i]);
			dp_p_0 = temp;
		}
		return dp_i_0;
	}
};

4.字符串解码

题目描述:给定一个字符串,k[string]表示string重复了k次,k保证为正整数,认为输入的字符串总是有效的。

示例:

s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".

思路:看到括号匹配,首先就应该想到栈。制作一个类似于分配律的计算器。如3[a2[c]b] 使用一次分配律-> 3[accb] 再使用一次分配律->accbaccbaccb

2遍心得:1.用两个栈,一个存数字(重复的次数),另一个栈存已经遍历过的正确的字符串。

                 2.其实就是遍历字符串,每个字符有四种类型,每一种怎么处理写清楚就好了。

解题步骤:

1.针对s中的每一个元素

                  2.如果该元素是数字,更新num,num = num*10+s[i]-'0';

                  3.如果是字母,把它加到结果字符串后面;

                  4.如果当前元素是'[',把num压入栈nums中,num置0,把结果字符串压入strs栈,res置为""。

                  5.否则(只剩遇到']'了,操作与他匹配的‘[’中间的字符,使用分配律):定义次数为nums栈顶元素。nums出栈。

                                 6.重复上一步求的次数,strs栈的栈顶元素+=结果字符串

                                 7.结果字符串 = strs栈的栈顶元素。

                                 8.strs出栈

class Solution {
public:
    string decodeString(string s) {
        int num = 0;
        string res = "";
        stack<int> nums;
        stack<string> strs;
        for(int i = 0;i<s.size();++i){
            if(s[i]>='0'&&s[i]<='9') num = num*10+s[i]-'0';
            else if((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z')) res += s[i];
            else if(s[i] == '['){
                nums.push(num);
                num = 0;
                strs.push(res);
                res = "";
            }
            else{
                int times = nums.top();
                nums.pop();
                for(int i = 0;i<times;++i) strs.top() += res;
                res = strs.top();
                strs.pop();
            }
        }
        return res;
    }
};

5.任务调度器

题目描述:给定一个用字符数组表示对额cpu需要执行的任务列表,每个任务都可以在1个单位时间内执行完。 

然而,两个相同的任务之间必须要有长度为n的冷却时间。因此至少又连续n个单位时间内cpu在执行不同的任务,或在待命状态。返回值是计算完成所有任务所需要的最短时间。

示例 1:

输入: tasks = ["A","A","A","B","B","B"], n = 2
输出: 8
执行顺序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
注:

任务的总个数为 [1, 10000]。
n 的取值范围为 [0, 100]。

解题步骤:

1.计算每个任务出现的次数

2.找出出现次数最多的任务,假设出现次数为x

3.计算至少需要的时间(x-1)*(n+1),记为min_time....我觉得不应该叫至少需要的时间,这种叫法完全不对,这个时间是除了最后执行那次调度之外,全部时间之和。

4.计算出现次数为x的任务总数count,计算最终结果为min_time+count

特殊情况:如果返回的结果小于数组长度,返回数组长度。AAABBBCCDD

class Solution {
public:
    struct com_by_value{
        bool operator()(pair<char,int>& a,pair<char,int>& b){ return a.second>b.second;}
    };
    int leastInterval(vector<char>& tasks, int n) {
        map<char,int> zyw;
        for(int i = 0;i<tasks.size();++i){
            zyw[tasks[i]]++;
        }
        vector<pair<char,int>> paixu(zyw.begin(),zyw.end());
        sort(paixu.begin(),paixu.end(),[](pair<char,int>& a,pair<char,int>& b){ return a.second>b.second;});
        int k = paixu[0].second;
        int count = 0;
        for(auto ch:paixu){
            if(k == ch.second) count++;
        }
        int res  = count+(n+1)*(k-1);
        return res>tasks.size()?res:tasks.size();
    }
};

6.单词拆分              此题比较难

题目描述:给定一个非空字符串和一个非空单词列表的字典,判定s是否可以被空格拆分成一个或多个在字典中出现的单词。

拆分时可以重复使用字典中的单词。可以假设字典中没有重复的单词。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"。
示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以被拆分成 "apple pen apple"。
     注意你可以重复使用字典中的单词。
示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

思路:字典树加速匹配速度,记忆化搜索减少重复计算

1.定义字典树节点数据结构,一个标志位,一个map,map<char,TrieNode*> next容器,构造函数默认标志位为false

1.对字典中的每个元素,把root赋给新创建的node

           2.对每个元素中的每个字符,如果node的next中遇到没有出现过的字符:那就node->next[c] = 新建TrieNode.

           3.node指向node的next

4.把node的标志位置为true

5.返回func,里面三个参数,字符串,开始位置,结束位置。

           6.如果开始等于结束,true

           7.如果memo容器中start位置元素不为0,如果>0返回true,<0返回false

            8.对start到end中的每个元素,如果node的next中s[i]元素没出现过,break

            9.node指向下一个

             10.如果node的标志位是true,并且递归i+1,end也为真,那就把memo[start]置为1,返回true

10.memo[start]置为-1,返回false

class Solution {
public:
    struct TrieNode{
        bool flag;
        map<char,TrieNode*> next;
        TrieNode():flag(false) {}
    };
    vector<int> memo;
    TrieNode* root;
    bool func(string& s,int start,int end){
        if(start == end) return true;
        if(memo[start] != 0){ return memo[start]>0;}
        auto node = root;
        for(int i = start;i<end;++i){
            if(node->next.count(s[i]) == 0) break;
            node = node->next[s[i]];
            if(node->flag&&func(s,i+1,end)) {
                memo[start] = 1;
                return true;
            }
        }
        memo[start] = -1;
        return false;
    }
    bool wordBreak(string s, vector<string>& wordDict) {
        root= new TrieNode();
        auto node = root;
        memo = vector<int>(s.size(),0);
        //TrieNode* node = new TrieNode();
        for(auto ch:wordDict){
            node = root;
            for(auto k:ch){
                if(node->next.count(k) == 0) node->next[k] = new TrieNode();
                node = node->next[k];
            }
            node->flag = true;
        }
        return func(s,0,s.size());
    }
};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值