自动机解决股票买卖问题(Best Time to Buy and Sell Stock I II III IV等)详细教程

博客先介绍有穷自动机,包括其状态集合、状态转换,分为确定和非确定自动机,并以字母规则为例说明。接着围绕股票买卖问题,给出LeetCode多道相关题目,如允许一次、多次、最多两次、最多K次交易,以及有冷却期、交易手续费等情况,还定义状态并给出代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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;
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值