714. best time to buy and sell stock with transaction fee

Question:

Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.

You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)

Return the maximum profit you can make.

Example 1:

1
2
3
4
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1Selling at prices[3] = 8Buying at prices[4] = 4Selling at prices[5] = 9The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

Note:

0 < prices.length <= 50000.

0 < prices[i] < 50000.

0 <= fee < 50000.

Answer:

1
2
3
4
5
6
7
8
9
10
11
12
class {
public int maxProfit(int[] prices, int fee) {
int buy[] = new int[prices.length], sell[] = new int[prices.length];
buy[0] = -prices[0];
sell[0] = 0;
for(int i=1; i<prices.length; i++){
buy[i] = Math.max(buy[i-1], sell[i-1] - prices[i]);
sell[i] = Math.max(sell[i-1], buy[i-1] + prices[i] - fee);
}
return sell[prices.length-1];
}
}

Running time: O(n), 20ms

Explanation

Given any day I, its max profit status boils down to one of the two status below:

(1) buy status:
buy[i] represents the max profit at day i in buy status, given that the last action you took is a buy action at day K, where K<=i. And you have the right to sell at day i+1, or do nothing.
(2) sell status:
sell[i] represents the max profit at day i in sell status, given that the last action you took is a sell action at day K, where K<=i. And you have the right to buy at day i+1, or do nothing.

Let’s walk through from base case.

Base case:
We can start from buy status, which means we buy stock at day 0.
buy[0]=-prices[0];
Or we can start from sell status, which means we sell stock at day 0.
Given that we don’t have any stock at hand in day 0, we set sell status to be 0.
sell[0]=0;

Status transformation:
At day i, we may buy stock (from previous sell status) or do nothing (from previous buy status):
buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]);
Or
At day i, we may sell stock (from previous buy status) or keep holding (from previous sell status):
sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);

Finally:
We will return sell[last_day] as our result, which represents the max profit at the last day, given that you took sell action at any day before the last day.

Other Understanding

Define dp array:
hold[i] : The maximum profit of holding stock until day i;
notHold[i] : The maximum profit of not hold stock until day i;

dp transition function:
For day i, we have two situations:

  1. Hold stock:
    (1) We do nothing on day i: hold[i - 1];
    (2) We buy stock on day i: notHold[i - 1] - prices[i];
  2. Not hold stock:
    (1) We do nothing on day i: notHold[i - 1];
    (2) We sell stock on day i: hold[i - 1] + prices[i] - fee;

O(n) space

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int maxProfit(int[] prices, int fee) {
if (prices == null || prices.length <= 1) {
return 0;
}
int len = prices.length;
int[] hold = new int[len]; //Till day i, the max profit is hold[i] if I hold the stock.
int[] notHold = new int[len]; //Till day i, the max profit is notHold[i] if I do not hold the stock.
hold[0] = -prices[0];
notHold[0] = 0;
for (int i = 1; i < prices.length; i++) {
hold[i] = Math.max(hold[i - 1], notHold[i - 1] - prices[i]);
notHold[i] = Math.max(notHold[i - 1], hold[i - 1] - fee + prices[i]);
}
return notHold[len - 1];
}
}

O(1) Space
From the dp transition function, we can see the i th state are only based on the i-1 th state. So we could optimize space to O(1) using two variables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class {
public int maxProfit(int[] prices, int fee) {
if (prices == null || prices.length <= 1) {
return 0;
}
int len = prices.length;
int hold = -prices[0];
int notHold = 0;
for (int i = 1; i < prices.length; i++) {
hold = Math.max(hold, notHold - prices[i]);
notHold = Math.max(notHold, hold + prices[i] - fee);
}
return notHold;
}
}