文章目录
- (一)自动机概述
- (二)leetcode 121: Best Time to Buy and Sell Stock
- (三)leetcode 122: Best Time to Buy and Sell Stock II
- (四)leetcode 123: Best Time to Buy and Sell Stock III
- (五)leetcode 188: Best Time to Buy and Sell Stock IV
- (六)leetcode 309: Best Time to Buy and Sell Stock with Cooldown
- (七)leetcode 714: Best Time to Buy and Sell Stock with Transaction Fee
Best Time to Buy and Sell Stock系列问题在面试过程中经常遇到,这里将下面几种问题做一个总结。
(一)自动机概述
有穷自动机(FA)首先包含一个有限状态的集合,还包含了从一个状态到另外一个状态的转换。有穷自动机看上去就像是一个有向图,其中状态是图的节点,而状态转换则是图的边。此外这些状态中还必须有一个初始状态和至少一个接受状态。根据状态转移的性质又分为确定的自动机(DFA)和非确定的自动机(NFA)。
考虑仅由字母{a, b}组成的字符串,要求字符串中字母b必须成对出现,否则字符串非法。这个规则实现起来其实非常简单,不需要自动机也完全可以。但是我们考虑使用自动机来进行判断。顺便,该规则的正规表达式描述是:(a|bb)*。星号运算代表重复若干次,包括零次。
我们考虑做一个图来表示描述该规则的DFA。令状态1为初始状态,显然在状态1上,我们还没有违反规则。因此,经过字母a以后我们还可以回到状态1。经过字母b就不能回到状态1了,此时需要一个新状态,令其为2。
状态2表示“待定的”状态,在这个状态时不能肯定字符串是非法的,但也不是合法的。在状态2上,如果经过字母b,就回到了合法的状态也就是状态1,;如果经过字母a,则该字符串肯定是非法的。建立一个新状态即状态3,用于表示非法状态。
状态3比较简单,已经到了非法状态,其后的任何字母都不会改变这个状态了。因此,该DFA可以表示如下:
(二)leetcode 121: Best Time to Buy and Sell Stock
题意:给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做恰好一次交易,求最大收益。
定义sell表示截止到当天,卖之后能得到的最大收益
定义buy表示截止到当天,买之后能得到的最大收益
状态转换图:
代码如下:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0) return 0;
int buy = -prices[0],sell = 0; //初始化i=0时的状态
for(int i=1;i<prices.length;i++){ //对于每一天,都更新状态
sell = Math.max(sell, buy+prices[i]);
buy = Math.max(buy, -prices[i]);
}
return sell;
}
}
(三)leetcode 122: Best Time to Buy and Sell Stock II
题意:给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做无数次交易,求最大收益。
定义sell表示截止到当天,卖之后能得到的最大收益
定义buy表示截止到当天,买之后能得到的最大收益
状态图:
代码如下:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0) return 0;
int buy = -prices[0],sell = 0;
for(int i=0;i<prices.length;i++){
int tmp = sell; //保持上一次的sell ,表示以前卖挣的钱
sell = Math.max(sell, buy+prices[i]); //卖的状态不变
buy = Math.max(buy, tmp-prices[i]); //买的状态多了一次从sell到buy
}
return sell;
}
}
(四)leetcode 123: Best Time to Buy and Sell Stock III
题意:给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做最多两次交易,求最大收益。
定义sell0表示截止到当天,第一次卖之后能得到的最大收益
定义buy0表示截止到当天,第一次买之后能得到的最大收益
定义sell1表示截止到当天,第二次卖之后能得到的最大收益
定义buy1表示截止到当天,第二次买之后能得到的最大收益
状态图如上图一样,只是从buy–>sell,再从sell->buy最多就一次,不能无数次交易了。
代码如下:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0) return 0;
int buy0 = -prices[0],sell0 = 0; //第一次买卖
int buy1 = -Integer.MIN_VALUE,sell1=0; //第二次买卖
for(int i=0;i<prices.length;i++){
sell0 = Math.max(sell0, buy0+prices[i]);
buy0 = Math.max(buy0, -prices[i]); // 0-prices[i]
sell1 = Math.max(sell1, buy1+prices[i]);
buy1 = Math.max(buy1, sell0-prices[i]); //亏钱买入
}
return Math.max(sell0, sell1);
}
}
(五)leetcode 188: Best Time to Buy and Sell Stock IV
题意:给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做最多K次交易,求最大收益。
状态图如上图所示:只是从buy–>sell,再从sell->buy最多就K次,不能无数次交易了。
定义sell[i]表示截止到当天i,卖之后能得到的最大收益
定义buy[i]表示截止到当天i,买之后能得到的最大收益
代码:
class Solution {
public int maxProfit(int k, int[] prices) {
if(k==0 || prices.length==0) return 0;
if(2*k >= prices.length) return umLimit(prices);
int[] buy = new int[k+1]; //进行k次买卖
Arrays.fill(buy, Integer.MIN_VALUE); //初始化最小
buy[1] = -prices[0];
int[] sell = new int[k+1];
for(int i=1;i<prices.length;i++){
for(int j=1;j<k+1;j++){
int tmp = sell[j-1];
sell[j] = Math.max(sell[j], buy[j]+prices[i]);
buy[j] = Math.max(buy[j], tmp-prices[i]);
}
}
return Arrays.stream(sell).max().getAsInt(); //返回sell[]中的最大值
}
private int umLimit(int[] prices) { //k次买卖比给定的数组长度还大,因此特殊考虑
int sell = 0,buy = -prices[0]; //初始化一下
for(int i=1;i<prices.length;i++){
int tmp = sell;
sell = Math.max(sell, buy+prices[i]);
buy = Math.max(buy, tmp-prices[i]);
}
return sell;
}
}
(六)leetcode 309: Best Time to Buy and Sell Stock with Cooldown
题意:给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做无数次交易,但每次卖出之后必须至少休息一天,求最大收益。
定义sell表示截止到当天,卖之后能得到的最大收益
定义buy表示截止到当天,买之后能得到的最大收益
定义rest表示截止到当天,休息之后能得到的最大收益
代码:
class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0) return 0;
int buy = -prices[0];
int sell = 0,rest=0;
for(int i=1;i<prices.length;i++){
int tmp = rest;
rest = Math.max(sell, rest);
sell = Math.max(sell, buy+prices[i]);
buy = Math.max(buy, tmp-prices[i]);
}
return Math.max(rest, sell);
}
}
(七)leetcode 714: Best Time to Buy and Sell Stock with Transaction Fee
给定一个数组prices,prices[i]表示股票在第i天的价格(买入or卖出),在卖出股票之前必须先买进,且手中不能同时买进两份股票,现在允许做无数次交易,但每次卖出需要支付一定的手续费,求最大收益。
定义sell表示截止到当天,卖之后能得到的最大收益
定义buy表示截止到当天,买之后能得到的最大收益
代码:
class Solution {
public int maxProfit(int[] prices, int fee) { //题目已经限制prices.length和fee的范围了
int sell = 0;
int buy = -prices[0];
for(int i=0;i<prices.length;i++){
int tmp = sell;
sell = Math.max(sell, buy+prices[i]-fee); //挣的钱要减去交易费用
buy = Math.max(buy,tmp-prices[i]);
}
return sell;
}
}