头图

Path problem

2021.05.13

No.514 Freedom Road

In the video game "Fallout 4", the mission "Lead to Freedom" requires players to reach a metal dial called "Freedom Trail Ring" and use the dial to spell specific keywords to open the door.​
Given a string ring, it represents the code engraved on the outer ring; given another string key, it represents the keyword that needs to be spelled. You need to figure out the minimum number of steps to be able to spell all the characters in the keyword.

Initially, the first character of ring is aligned with the 12:00 direction. You need to rotate the ring clockwise or counterclockwise to align one character of the key at 12:00, and then press the center button to spell out all the characters in the key one by one.

In the stage of rotating the ring to spell out the key character key[i]:

You can rotate the ring one position clockwise or counterclockwise, which counts as 1 step. The ultimate goal of the rotation is to align a character of the string ring with the 12:00 direction, and this character must be equal to the character key[i].
If the character key[i] has been aligned to the 12:00 direction, you need to press the center button to spell it, which will also count as 1 step. After pressing, you can start to spell the next character of key (the next stage) until all spelling is completed.
Example:

图片

 
Input: ring = "godding", key = "gd"
Output: 4
explain:
For the first character'g' of key, it is already in the correct position, we only need 1 step to spell this character.
For the second character'd' of the key, we need to rotate the ring "godding" 2 steps counterclockwise to make it "ddinggo".
Of course, we need one more step to spell.
So the final output is 4.
hint:

The string length of ring and key ranges from 1 to 100;
There are only lowercase characters in the two strings, and there may be repeated characters;
The string key must be spelled out by rotating the string ring.

Source: LeetCode
Link: https://leetcode-cn.com/problems/freedom-trail
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=514 lang=javascript
 *
 * [514] 自由之路
 */

// @lc code=start
/**
 * @param {string} ring
 * @param {string} key
 * @return {number}
 */
var findRotateSteps = function(ring, key) {
    // 用于存储ring中的索引信息
    const keyMap = {};

    for(let i = 0; i < ring.length; i++) {
        const k = ring[i];
        if(keyMap[k]) {
            keyMap[k].push(i)
        } else {
            keyMap[k] = [i]
        }
    }

    // 缓存用于dfs剪枝
    const memo = new Array(ring.length);

    for(let i = 0; i < ring.length; i++) {
        memo[i] = new Array(key.length).fill(-1)
    }
    
    // dfs递归
    const dfs = ( ringI, keyI ) => {
        if(keyI == key.length) return 0;

        // 剪枝 有缓存直接返回缓存的结果
        if( memo[ringI][keyI] !== -1 ) return memo[ringI][keyI]

        const cur = key[keyI];

        // 返回的结果
        let res = Infinity;
        for(const targetI of keyMap[cur]) {
            // 正向位置
            let d1 = Math.abs(ringI - targetI),
                d2 = ring.length - d1;
            const curMin = Math.min(d1, d2)
            // 递归的循环不变式
            res = Math.min(res, curMin + dfs(targetI, keyI+1))
        }
        memo[ringI][keyI] = res;
        return res;
    }

    return dfs(0,0) + key.length;

};

Dynamic planning, the key is to find the pruning optimization plan


2021.05.16

No.576 The number of the routes out of bounds

Given an m × n grid and a sphere. The starting coordinates of the ball are (i,j), you can move the ball to the adjacent cell, or move up, down, left, and right to make the ball cross the grid boundary. However, you can move up to N times. Find the number of paths that can move the ball out of the boundary. The answer may be very large, and the result mod 109 + 7 is returned.​
 

Example 1:

Input: m = 2, n = 2, N = 2, i = 0, j = 0
Output: 6
explain:

图片

Example 2:

Input: m = 1, n = 3, N = 3, i = 0, j = 1
Output: 12
explain:

图片

instruction:

Once the ball is out of bounds, it cannot be moved back into the grid.
The length and height of the grid are in the range of [1,50].
N is in the range of [0,50].

Source: LeetCode
Link: https://leetcode-cn.com/problems/out-of-boundary-paths
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=576 lang=javascript
 *
 * [576] 出界的路径数
 */

// @lc code=start
/**
 * @param {number} m
 * @param {number} n
 * @param {number} maxMove
 * @param {number} startRow
 * @param {number} startColumn
 * @return {number}
 */
var findPaths = function(m, n, N, i, j) {
    const helper = (i, j, N) => {
        // N 次用完了,此时不能再走了就返回 0
        if (N < 0) {
            return 0
        }
        // 出界条件满足了,说明有一条出界路径,就返回 1
        if (i < 0 || i >= m || j < 0 || j >= n) {
            return 1
        }
        // 记忆化搜索(如果重复访问了那就用之前的值)
        const key = `${i}-${j}-${N}`
        if (visited.has(key)) {
            return visited.get(key)
        }
        let res = 0
        // 找上、下、左、右 四个方向
        for (let k = 0; k < 4; k++) {
            res = (res + helper(i + direction[k][0], j + direction[k][1], N -1)) % mod
        }
        // 将当前的值缓存下来
        visited.set(key, res)
        return res
    }
    const mod = Math.pow(10, 9) + 7
    const direction = [[1, 0], [-1, 0], [0, -1], [0, 1]]
    const visited = new Map()
    return helper(i, j, N)
};

Using Map to make recursive judgments, state transition is an exploration in four directions


2021.05.17

No.980 Different paths-iii

On a two-dimensional grid, there are 4 types of grids:​
1 represents the starting square. And there is only one starting square.
2 means the end square, and there is only one end square.
0 means an empty square we can walk through.
-1 means an obstacle we cannot overcome.
Returns the number of different paths from the start square to the end square when walking in four directions (up, down, left, and right).

Each barrier-free square must be passed once, but the same square cannot be repeated in a path.

 

Example 1:

Input: [[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
Output: 2
Explanation: We have the following two paths:

  1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
  2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
    Example 2:

Input: [[1,0,0,0],[0,0,0,0],[0,0,0,2]]
Output: 4
Explanation: We have the following four paths:

  1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
  2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
  3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
  4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
    Example 3:

Input: [[0,1],[2,0]]
Output: 0
explain:
No road can pass through every empty square exactly once.
Please note that the start and end squares can be located anywhere in the grid.
 

hint:

1 <= grid.length * grid[0].length <= 20

Source: LeetCode
Link: https://leetcode-cn.com/problems/unique-paths-iii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=980 lang=javascript
 *
 * [980] 不同路径 III
 */

// @lc code=start
/**
 * @param {number[][]} grid
 * @return {number}
 */
var uniquePathsIII = function(grid) {
    if(!grid.length) return 0;

    const helper = ( grid, i, j, step ) => {
        if( i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == -1 ) {
            return 0;
        }

        if(grid[i][j] == 2) return step == -1 ? 1 : 0;

        grid[i][j] = -1;

        let res = 0;

        // 向四个方向探索
        res += helper( grid, i+1, j, step - 1 );
        res += helper( grid, i, j+1, step - 1 );
        res += helper( grid, i-1, j, step - 1 );
        res += helper( grid, i, j-1, step - 1 );

        grid[i][j] = 0

        return res;
    }

    let startI = 0, startJ = 0, total = 0;

    // 遍历
    for( let i = 0; i < grid.length; i++ ) {
        for( let j = 0; j < grid[i].length; j++ ) {
            if( grid[i][j] == 1 ) {
                startI = i;
                startJ = j;
            }

            if( grid[i][j] == 0 ) {
                total++;
            }
        }
    }

    return helper(grid, startI, startJ, total);
};

Same as the 576 question, the difference is that it cannot be returned. Dynamic programming conducts exploration in four directions.


2021.05.18

No.1129 The shortest path of alternating colors

In a directed graph, the nodes are marked as 0, 1, ..., n-1. Each edge in this graph is either red or blue, and there are self-loops or parallel edges.​
Each [i, j] pair in red_edges represents the red directed edge from node i to node j. Similarly, each [i, j] pair in blue_edges represents the blue directed edge from node i to node j.

Returns an array answer of length n, where answer[X] is the length of the shortest path from node 0 to node X where the red and blue sides alternate. If there is no such path, then answer[x] = -1.

 

Example 1:

Input: n = 3, red_edges = [[0,1],[1,2]], blue_edges = []
Output: [0,1,-1]
Example 2:

Input: n = 3, red_edges = [[0,1]], blue_edges = [[2,1]]
Output: [0,1,-1]
Example 3:

Input: n = 3, red_edges = [[1,0]], blue_edges = [[2,1]]
Output: [0,-1,-1]
Example 4:

Input: n = 3, red_edges = [[0,1]], blue_edges = [[1,2]]
Output: [0,1,2]
Example 5:

Input: n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]]
Output: [0,1,1]
 

hint:

1 <= n <= 100
red_edges.length <= 400
blue_edges.length <= 400
red_edges[i].length == blue_edges[i].length == 2
0 <= red_edgesi, blue_edgesi < n

Source: LeetCode
Link: https://leetcode-cn.com/problems/shortest-path-with-alternating-colors
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=1129 lang=javascript
 *
 * [1129] 颜色交替的最短路径
 */

// @lc code=start
/**
 * @param {number} n
 * @param {number[][]} red_edges
 * @param {number[][]} blue_edges
 * @return {number[]}
 */
var shortestAlternatingPaths = function(n, red_edges, blue_edges) {
    var re_0=new Array(n).fill(Number.MAX_VALUE);
    var re_1=new Array(n).fill(Number.MAX_VALUE);

   
    var graph_red=new Array(n);
    var graph_blue=new Array(n);
    for(var i=0;i<n;i++){
        graph_red[i]= new Array();
        graph_blue[i]=new Array();

    }
    for(var i=0;i<red_edges.length;i++){
        graph_red[red_edges[i][0]].push(red_edges[i][1]);
    }
    for(var i=0;i<blue_edges.length;i++){
        graph_blue[blue_edges[i][0]].push(blue_edges[i][1]);
    }

    re_1[0]=0;
    re_0[0]=0;
    var now_b=[0],now_r=[0];
    step=0;
    while(now_b.length!==0 ||now_r.length!==0){
        var new_b=[], new_r=[];
        var point,adj;
        step++;
        while(now_b.length!==0){
            point=now_b.pop();
            adj=graph_red[point];
            for(var next of adj){
                if(re_0[next]===Number.MAX_VALUE){
                    re_0[next]=step;
                    new_r.push(next);
                }
            }
        }
        while(now_r.length!=0){
            point=now_r.pop();
            adj=graph_blue[point];
            for(var next of adj){
                if(re_1[next]===Number.MAX_VALUE){
                    re_1[next]=step;
                    new_b.push(next);
                }
            }
        }
        now_r=new_r;
        now_b=new_b;
    }
    //console.log(re_0,re_1);
    for(var i=0;i<n;i++){
        re_0[i]=Math.min(re_0[i],re_1[i]);
        if(re_0[i]===Number.MAX_VALUE) re_0[i]=-1;
    }

    return re_0;

};

Dijistra algorithm is modified, using dynamic programming for stepwise bfs


Summarize:
  1. The most common path problem is backtracking exploration, the key is pruning optimization, the summary of the state transition function;
  2. Dynamic programming is a way of dealing with problems step by step. Commonly used data structures such as two-dimensional arrays and hashMap are used for processing.

Stock issue

2021.05.19

No.121 The best time to buy and sell stocks

Given an array prices, its i-th element prices[i] represents the price of a given stock on the i-th day.​
You can only choose to buy this stock on a certain day, and choose to sell the stock on a different day in the future. Design an algorithm to calculate the maximum profit you can get.

Return the maximum profit you can get from this transaction. If you cannot make any profit, return 0.

 

Example 1:

Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on the 2nd day (stock price = 1), and sell on the 5th day (stock price = 6). Maximum profit = 6-1 = 5.

 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

Example 2:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is completed, so the maximum profit is 0.
 

hint:

1 <= prices.length <= 105
0 <= prices[i] <= 104

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=3 lang=javascript
 *
 * [3] 无重复字符的最长子串
 */

// @lc code=start
/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLongestSubstring = function(s) {
    let max = 0, index = 0;

    for(let i=0,j=0;j<s.length;j++) {
        index = s.slice(i,j).indexOf(s[j]);
        
        if(isRepeat(s.slice(i,j))) {
            i += index + 1;
        }

        max = Math.max(max, j - i + 1)
    }

    return max;

    function isRepeat(s) {
        return s.length == Array.from(new Set(s.split(''))).length;
    }
};

Dynamic programming, stock problem i


2021.05.20

No.122 The best time to buy and sell stocks-ii

Given an array prices, where prices[i] is the price of a given stock on the i day.​
Design an algorithm to calculate the maximum profit you can get. You can complete as many transactions as possible (buying and selling a stock multiple times).

Note: You cannot participate in multiple transactions at the same time (you must sell the previous stocks before buying again).

 

Example 1:

Input: prices = [7,1,5,3,6,4]
Output: 7
Explanation: Buy on the 2nd day (stock price = 1) and sell on the 3rd day (stock price = 5). This exchange can make a profit = 5-1 = 4.
Then, buy on the 4th day (stock price = 3), and sell on the 5th day (stock price = 6). This exchange can make a profit = 6-3 = 3.
Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on the 1st day (stock price = 1) and sell on the 5th day (stock price = 5). This exchange can make a profit = 5-1 = 4.
Note that you cannot buy stocks one after another on the first day and the second day, and then sell them later. Because this is involved in multiple transactions at the same time, you must sell the previous stocks before buying again.
Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is completed, so the maximum profit is 0.
 

hint:

1 <= prices.length <= 3 * 104
0 <= prices[i] <= 104

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=122 lang=javascript
 *
 * [122] 买卖股票的最佳时机 II
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let profit_in = -prices[0],
        profit_out = 0,
        n = prices.length;
    for(let i =0; i < n; i++) {
        profit_out = Math.max(profit_out, profit_in + prices[i]);
        profit_in = Math.max(profit_in,  profit_out - prices[i]);
    }
        
    return profit_out;  
};

Dynamic programming, stock problem ii


2021.05.21

No.123 The best time to buy and sell stocks-iii

Given an array, its i-th element is the price of a given stock on the i-th day.​
Design an algorithm to calculate the maximum profit you can get. You can complete up to two transactions.

Note: You cannot participate in multiple transactions at the same time (you must sell the previous stocks before buying again).

 

Example 1:

Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on the 4th day (stock price = 0) and sell on the 6th day (stock price = 3). This exchange can make a profit = 3-0 = 3.
Then, buy on the 7th day (stock price = 1) and sell on the 8th day (stock price = 4). This exchange can make a profit = 4-1 = 3.
Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on the 1st day (stock price = 1) and sell on the 5th day (stock price = 5). This exchange can make a profit = 5-1 = 4.
Note that you cannot buy stocks one after another on the first day and the second day, and then sell them later.
Because this is involved in multiple transactions at the same time, you must sell the previous stocks before buying again.
Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is completed, so the maximum profit is 0.
Example 4:

Input: prices = [1]
Output: 0
 

hint:

1 <= prices.length <= 105
0 <= prices[i] <= 105

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=123 lang=javascript
 *
 * [123] 买卖股票的最佳时机 III
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    //第一次 买入, 卖出的利润
    let profit_1_in = -prices[0], profit_1_out = 0;
    //继第一次之后,第二次买入卖出的利润
    let profit_2_in = -prices[0], profit_2_out = 0;
    let n = prices.length;
    for (let i = 1; i < n; i++){
        profit_2_out = Math.max(profit_2_out, profit_2_in + prices[i]);
        //第二次买入后的利润, 第一次卖出的利润 - prices[i]
        profit_2_in = Math.max(profit_2_in, profit_1_out - prices[i]);
        profit_1_out = Math.max(profit_1_out, profit_1_in + prices[i]);
        //第一次买入后,利润为 -prices[i]
        profit_1_in = Math.max(profit_1_in, -prices[i]);
    }
    return profit_2_out;
};

Dynamic programming, stock issues iii


2021.05.24

No.188 The best time to buy and sell stocks-iv

Given an integer array prices, its i-th element prices[i] is the price of a given stock on the i-th day.​
Design an algorithm to calculate the maximum profit you can get. You can complete up to k transactions.

Note: You cannot participate in multiple transactions at the same time (you must sell the previous stocks before buying again).

 

Example 1:

Input: k = 2, prices = [2,4,1]
Output: 2
Explanation: Buy on the 1st day (stock price = 2) and sell on the 2nd day (stock price = 4). This exchange can make a profit = 4-2 = 2.
Example 2:

Input: k = 2, prices = [3,2,6,5,0,3]
Output: 7
Explanation: Buy on the 2nd day (stock price = 2) and sell on the 3rd day (stock price = 6). The exchange can make a profit = 6-2 = 4.

 随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

 

hint:

0 <= k <= 100
0 <= prices.length <= 1000
0 <= prices[i] <= 1000

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=188 lang=javascript
 *
 * [188] 买卖股票的最佳时机 IV
 */

// @lc code=start
/**
 * @param {number} k
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(k, prices) {
    let n = prices.length;
    if (k > n / 2) {
        k = Math.floor(n/2);
    }
    let profits = [];
    for(let j=0; j <= k; j++) {
        profits[j] = {
            profits_out: 0,
            profits_in: -prices[0]
        }
    }
    
    for( let i = 0; i < n; i++ ) {
        for( let j=1; j <= k; j++ ) {
            profits[j] = {
                profits_out: Math.max(profits[j][`profits_out`], profits[j][`profits_in`] + prices[i]),
            profits_in: Math.max(profits[j][`profits_in`], profits[j-1][`profits_out`] - prices[i])
            }
        }
    }

    return profits[k][`profits_out`];
};

Dynamic programming, stock problem iv


2021.05.26

No.309 The best time to buy and sell stocks includes a freezing period

Given an integer array, the i-th element represents the stock price on the i-th day.​
Design an algorithm to calculate the maximum profit. Under the following constraints, you can complete as many transactions as possible (buying and selling a stock multiple times):

You cannot participate in multiple transactions at the same time (you must sell the previous stock before buying again).
After selling the stock, you cannot buy the stock the next day (ie, the freezing period is 1 day).
Example:

Input: [1,2,3,0,2]
Output: 3
Explanation: The corresponding transaction status is: [Buy, Sell, Freeze Period, Buy, Sell]

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=309 lang=javascript
 *
 * [309] 最佳买卖股票时机含冷冻期
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let n = prices.length,
        profits_in = -prices[0],
        profits_out = 0,
        profits_freeze = 0;
    
    for( let i = 0; i < prices.length; i++ ) {
        let temp = profits_out;
        profits_out = Math.max(profits_out, prices[i] + profits_in);
        profits_in = Math.max(profits_in, profits_freeze - prices[i]);
        profits_freeze = temp;
    }

    return profits_out;
};

Dynamic programming, stock problem v


2021.05.27

No.714 The best time to buy and sell stocks includes handling fees

Given an integer array prices, the i-th element represents the stock price on the i-th day; the integer fee represents the transaction fee for stocks.​
You can complete transactions an unlimited number of times, but you need to pay a handling fee for each transaction. If you have already purchased a stock, you can no longer buy stocks before you sell it.

Returns the maximum profit obtained.

Note: A transaction here refers to the entire process of buying, holding and selling stocks. You only need to pay a handling fee for each transaction.

 

Example 1:

Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit that can be achieved:
Buy here prices[0] = 1
Sell ​​here prices[3] = 8
Buy here prices[4] = 4
Sell ​​here prices[5] = 9
Total profit: ((8-1)-2) + ((9-4)-2) = 8
Example 2:

Input: prices = [1,3,7,5,10,3], fee = 3
Output: 6
 

hint:

1 <= prices.length <= 5 * 104
1 <= prices[i] < 5 * 104
0 <= fee < 5 * 104

Source: LeetCode
Link: https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=714 lang=javascript
 *
 * [714] 买卖股票的最佳时机含手续费
 */

// @lc code=start
/**
 * @param {number[]} prices
 * @param {number} fee
 * @return {number}
 */
var maxProfit = function(prices, fee) {
    let n = prices.length,
        profits_in = 0 - prices[0],
        profits_out = 0;
    
    for( let i=0; i < prices.length; i++ ) {
        profits_out = Math.max(profits_out, prices[i] + profits_in - fee);
        profits_in = Math.max(profits_in, profits_out - prices[i]);
    }

    return profits_out < 0 ? 0 : profits_out;
};

Dynamic programming, stock issues vi


Summarize:
  1. The key to the stock issue lies in the judgment and transfer of the input and output status, and the use of dynamic programming ideas for processing;
  2. In the implementation of dynamic programming, the gradual iteration of multi-dimensional arrays is more common. For stock issues, dimensionality reduction can be performed to optimize efficiency.

Split combination

2021.05.31

No.132 Split palindrome-ii

Give you a string s, please split s into some substrings so that each substring is a palindrome.​
Returns the minimum number of splits that meet the requirements.

 

Example 1:

Input: s = "aab"
Output: 1
Explanation: You can split s into two palindrome substrings like ["aa","b"] only once.
Example 2:

Input: s = "a"
Output: 0
Example 3:

Input: s = "ab"
Output: 1
 

hint:

1 <= s.length <= 2000
s only consists of lowercase English letters

Source: LeetCode
Link: https://leetcode-cn.com/problems/palindrome-partitioning-ii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=132 lang=javascript
 *
 * [132] 分割回文串 II
 */

// @lc code=start
/**
 * @param {string} s
 * @return {number}
 */
var minCut = function (s) {
    let j, dp = new Array(s.length).fill(s.length)
    for (let i = 0; i < s.length; i++) {
        j = 0
        while (i - j >= 0 && i + j < s.length) {
            if (s[i - j] === s[i + j]) dp[i + j] = i - j === 0 ? 0 : dp[i + j] <= dp[i - j - 1] + 1 ? dp[i + j] : dp[i - j - 1] + 1
            else break
            j++
        }
        j = 0
        while (i - j >= 0 && i + j + 1 < s.length) {
            if (s[i - j] === s[i + j + 1]) dp[i + j + 1] = i - j === 0 ? 0 : dp[i + j + 1] <= dp[i - j - 1] + 1 ? dp[i + j + 1] : dp[i - j - 1] + 1
            else break
            j++
        }
    }
    return dp[s.length - 1]
};
Option II:
/*
 * @lc app=leetcode.cn id=132 lang=javascript
 *
 * [132] 分割回文串 II
 */

// @lc code=start
/**
 * @param {string} s
 * @return {number}
 */
var minCut = function (s) {
    var manacher = function(s){
    if(!s) return [];
    var lstRadios=[];
    //拼装manacher字符串
    s=s.replace(/\w/g,(a)=>{return '#'+a;})+'#';
    //三个指针,现在先定义中心指针c和最右指针r,剩下一个就是运动指针了
    var r=-1, c=-1;
    for(var i=0; i<s.length;i++){
        //判断i在不在r右侧
        //在,当前指针对应的半径无从判断,先赋值1
        //不在,就判断当前i以c为中心的对应指针i'的半径,
        //如果i'的半径超过了c半径的范围,就说明i的半径就为r-i+1;
        //如果i'的半径刚好在c半径的的范围内,且不在边界上,那么i的半径就为i'的半径
        //如果i'的半径刚好在c半径的指针上,那么i的半径是有可能再扩大的,需要再验证
        lstRadios[i]=r>i?Math.min(lstRadios[2*c-i],r-i+1):1;

        while(i+lstRadios[i]<s.length && i-lstRadios[i]>-1){
            if(s.charAt(i-lstRadios[i]) == s.charAt(i+lstRadios[i]))lstRadios[i]++;
            else break;
        }

        if(i+lstRadios[i]-1 > r){
            r=1+lstRadios[i]-1;
            c=i;
        }
    }
    return lstRadios;
};

if(s.length<=1) return 0;
    var lstRadios=manacher(s);
    var dp=[];
    for(var i=0; i<s.length; i++){
        if(dp[i] == undefined) dp[i]=i;
        if(i!=0)dp[i]=Math.min(dp[i-1]+1,dp[i]);
        //先以i为中点
        var d=lstRadios[2*i+1]/2 - 1;
        for(var j=1; j<=d; j++){
            if(dp[i+j] == undefined) dp[i+j]=i+j;
            dp[i+j]=Math.min(((i-j-1)>=0?(dp[i-j-1]+1):0),dp[i+j]);
        }
        //以i和i+1的中间为中心
        d=lstRadios[2*i+2]/2;
        if(d<=0)continue;
        for(var j=1; j<=d; j++){
            if(dp[i+j] == undefined) dp[i+j]=i+j;
            dp[i+j]=Math.min(((i-j)>=0?(dp[i-j]+1):0),dp[i+j]);
        }
    }
    return dp[s.length-1];

};
third solution:
/*
 * @lc app=leetcode.cn id=132 lang=javascript
 *
 * [132] 分割回文串 II
 */

// @lc code=start
/**
 * @param {string} s
 * @return {number}
 */
var minCut = function(s) {
    const n = s.length;
    const g = new Array(n).fill(0).map(() => new Array(n).fill(true));

    for (let i = n - 1; i >= 0; --i) {
        for (let j = i + 1; j < n; ++j) {
            g[i][j] = s[i] == s[j] && g[i + 1][j - 1];
        }
    }

    const f = new Array(n).fill(Number.MAX_SAFE_INTEGER);
    for (let i = 0; i < n; ++i) {
        if (g[0][i]) {
            f[i] = 0;
        } else {
            for (let j = 0; j < i; ++j) {
                if (g[j + 1][i]) {
                    f[i] = Math.min(f[i], f[j] + 1);
                }
            }
        }
    }

    return f[n - 1];
};

There are three different implementation schemes for dynamic programming: 1. Using the location characteristics of strings, only use one-time dynamic programming; 2. Using manacher to optimize one search; 3. Two-time dynamic programming


2021.06.01

No.1278 Split palindrome-iii

Give you a string s consisting of lowercase letters, and an integer k.​
Please split the string according to the following requirements:

First, you can modify some of the characters in s to other lowercase English letters.
Next, you need to split s into k non-empty and disjoint substrings, and each substring is a palindrome.
Please return the minimum number of characters that need to be modified to split the string in this way.

 

Example 1:

Input: s = "abc", k = 2
Output: 1
Explanation: You can split the string into "ab" and "c", and modify 1 character in "ab" to turn it into a palindrome.
Example 2:

Input: s = "aabbc", k = 3
Output: 0
Explanation: You can split the string into "aa", "bb" and "c", they are all palindrome strings.
Example 3:

Input: s = "leetcode", k = 8
Output: 0
 

hint:

1 <= k <= s.length <= 100
Only lowercase English letters are contained in s.

Source: LeetCode
Link: https://leetcode-cn.com/problems/palindrome-partitioning-iii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=1278 lang=javascript
 *
 * [1278] 分割回文串 III
 */

// @lc code=start
/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var palindromePartition = function(s, k) {
    const n = s.length;
    const dp = Array.from({length: n}, () => Array(k+1).fill(100));
    const cost = Array.from({length: n}, () => Array(n).fill(0));
    
    for (let len = 1; len < n; len++) {
        for (let i = 0; i < n - len; i++) {
            const j = i + len;
            cost[i][j] = cost[i+1][j-1] + (s[i] == s[j] ? 0 : 1);
        }
    }
    
    for (let i = 0; i < n; i++) {
        dp[i][1] = cost[0][i];
        for (let kk = 2; kk <= k; kk++) {
            for (let j = 0; j < i; j++) {
                dp[i][kk] = Math.min(dp[i][kk], dp[j][kk-1] + cost[j+1][i]);
            }
        }
    }
   
    return dp[n-1][k];
};
Option II:
/*
 * @lc app=leetcode.cn id=1278 lang=javascript
 *
 * [1278] 分割回文串 III
 */

// @lc code=start
/**
 * @param {string} s
 * @param {number} k
 * @return {number}
 */
var palindromePartition = function (s, k) {
  const m = s.length;
  const dp = Array.from(Array(k), () => Array(m));
  const map = Array.from(Array(m), () => Array(m));
  const helper = (i, j) => {
    const str = s.substring(i, j + 1);
    let res = 0;
    for (let l = 0; l < str.length / 2; l++)
      if (str[l] !== str[str.length - 1 - l]) res++;
    return res;
  };
  for (let i = 0; i < m; i++)
    for (let j = i; j < m; j++) map[i][j] = helper(i, j);

  for (let i = 0; i < k; i++) {
    for (let j = i; j < m; j++) {
      if (i === j || i === 0) {
        dp[i][j] = map[i][j]; // No need to remove, each char is a substring
        continue;
      }
      let res = Infinity;
      for (let k = 1; j - k >= i - 1; k++)
        res = Math.min(res, map[j + 1 - k][j] + dp[i - 1][j - k]);
      dp[i][j] = res;
    }
  }

  return dp[k - 1][m - 1];
};

Two dp, method 2 filters and intercepts the string


2021.06.02

No.1745 Palindrome string split-iv

Give you a string s, if it can be divided into three non-empty palindrome substrings, then return true, otherwise return false.​
When a string is read exactly the same as it is read backwards, it is called a palindrome string.

 

Example 1:

Input: s = "abcbdd"
Output: true
Explanation: "abcbdd" = "a" + "bcb" + "dd", all three substrings are palindrome.
Example 2:

Input: s = "bcbddxy"
Output: false
Explanation: s cannot be divided into 3 palindrome substrings.
 

hint:

3 <= s.length <= 2000
s contains only lowercase English letters.

Source: LeetCode
Link: https://leetcode-cn.com/problems/palindrome-partitioning-iv
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=1745 lang=javascript
 *
 * [1745] 回文串分割 IV
 */

// @lc code=start
/**
 * @param {string} s
 * @return {boolean}
 */
var checkPartitioning = function(s) {
    let dp = new Array(s.length);
    for (let i = 0; i < dp.length; i++) {
        dp[i] = new Array(s.length).fill(true);
    }

    for (let i = s.length - 1; i >=0 ;i--) {
        for (let j = i; j < s.length; j++) {
            if (i !== j) {
                dp[i][j] = (s[i] == s[j]) && dp[i + 1][j - 1];
            }
        }
    }
    for (let i = 0; i < s.length - 2; i++) {
        if(dp[0][i] == false) continue;
        for (let j = i + 1; j < s.length - 1; j++) {
            if(dp[i+1][j] == false || dp[j + 1][s.length - 1] == false ) continue;
            if (dp[0][i] && dp[i + 1][j] && dp[j + 1][s.length - 1]) {
                return true;
            }
        }
    }
    return false;
};

Dynamic planning, the cycle can be judged in advance


2021.07.02

No.139 Word Split

Given a non-empty string s and a list wordDict containing non-empty words, determine whether s can be split into one or more words that appear in the dictionary by spaces.​
instruction:

Words in the dictionary can be reused when splitting.
You can assume that there are no repeated words in the dictionary.
Example 1:

Input: s = "leetcode", wordDict = ["leet", "code"]
Output: true
Explanation: Return true because "leetcode" can be split into "leet code".
Example 2:

Input: s = "applepenapple", wordDict = ["apple", "pen"]
Output: true
Explanation: Return true because "applepenapple" can be split into "apple pen apple".
Note that you can reuse words in the dictionary.
Example 3:

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

Source: LeetCode
Link: https://leetcode-cn.com/problems/word-break
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=139 lang=javascript
 *
 * [139] 单词拆分
 */

// @lc code=start
/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {boolean}
 */
var wordBreak = function(s, wordDict) {
    const n = s.length;
    const wordDictSet = new Set(wordDict);
    const dp = new Array(n + 1).fill(false);
    dp[0] = true;
    for (let i = 1; i <= n; i++) {
        for (let j = 0; j < i; j++) {
            if (dp[j] && wordDictSet.has(s.substr(j, i - j))) {
                dp[i] = true;
                break;
            }
        }
    }
    return dp[n];
};

Typical dynamic programming problem, determine the cutting position through dp[i]


2021.07.07

No.140 Word Split-ii

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in the string to construct a sentence so that all the words in the sentence are in the dictionary. Return all these possible sentences.​
instruction:

Words in the dictionary can be reused when separating.
You can assume that there are no repeated words in the dictionary.
Example 1:

enter:
s = "catsanddog"
wordDict = ["cat", "cats", "and", "sand", "dog"]
Output:
[
  "cats and dog",
  "cat sand dog"
]
Example 2:

enter:
s = "pineapplepenapple"
wordDict = ["apple", "pen", "applepen", "pine", "pineapple"]
Output:
[
  "pine apple pen apple",
  "pineapple pen apple",
  "pine applepen apple"
]
Explanation: Note that you can reuse words in the dictionary.
Example 3:

enter:
s = "catsandog"
wordDict = ["cats", "dog", "sand", "and", "cat"]
Output:
[]

Source: LeetCode
Link: https://leetcode-cn.com/problems/word-break-ii
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=140 lang=javascript
 *
 * [140] 单词拆分 II
 */

// @lc code=start
/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {string[]}
 */
var wordBreak = function(s, wordDict) {
    let dp = new Array(s.length + 1).fill(false)
    dp[0] = true
    for (let i = 1; i <= s.length; i++) {
        for (let j = 0; j <= i; j++) {
            if (dp[j] && wordDict.indexOf(s.slice(j, i)) >= 0) {
                dp[i] = true
                break
            }
        }
    }
    if(!dp[s.length]) return []
    let ans = []
    function backTrack(cur, str) {
        if(str.length == 0) {
            ans.push(cur)
            return
        }
        for(let i = 0; i < str.length; i++) {
            if(wordDict.indexOf(str.slice(0, i + 1)) >= 0) {
                if(cur.length > 0) backTrack(cur + ' ' + str.slice(0, i + 1), str.slice(i + 1))
                else backTrack(str.slice(0, i + 1), str.slice(i + 1))
            }
        }
    }
    backTrack('', s)
    return ans
};
Option II:
/*
 * @lc app=leetcode.cn id=140 lang=javascript
 *
 * [140] 单词拆分 II
 */

// @lc code=start
/**
 * @param {string} s
 * @param {string[]} wordDict
 * @return {string[]}
 */
var wordBreak = function(s, wordDict) {
    // 辅助函数
    const backtrack = (s, length, wordSet, index, map) => {
        if (map.has(index)) {
            return map.get(index);
        }
        const wordBreaks = [];
        if (index === length) {
            wordBreaks.push([]);
        }
        for (let i = index + 1; i <= length; i++) {
            const word = s.substring(index, i);
            if (wordSet.has(word)) {
                const nextWordBreaks = backtrack(s, length, wordSet, i, map);
                for (const nextWordBreak of nextWordBreaks) {
                    const wordBreak = [word, ...nextWordBreak]
                    wordBreaks.push(wordBreak);
                }
            }
        }
        map.set(index, wordBreaks);
        return wordBreaks;
    }

    const map = new Map();
    const wordBreaks = backtrack(s, s.length, new Set(wordDict), 0, map);
    const breakList = [];
    for (const wordBreak of wordBreaks) {
        breakList.push(wordBreak.join(' '));
    }
    return breakList;
};

There are two options: 1. Use 139 questions to judge first, and then backtrack; 2. Use Map data structure to directly backtrack


2021.07.08

No.343 Integer Split

Given a positive integer n, split it into the sum of at least two positive integers, and maximize the product of these integers. Returns the largest product you can get.​
Example 1:

Input: 2
Output: 1
Explanation: 2 = 1 + 1, 1 × 1 = 1.
Example 2:

Input: 10
Output: 36
Explanation: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36.
Explanation: You can assume that n is not less than 2 and not more than 58.

Source: LeetCode
Link: https://leetcode-cn.com/problems/integer-break
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=343 lang=javascript
 *
 * [343] 整数拆分
 */

// @lc code=start
/**
 * @param {number} n
 * @return {number}
 */
var integerBreak = function(n) {
    // 利用均值不等式 x1 * x2 * ··· * xn ≤ ( ( x1 + x2 + ··· + xn ) / n )^n
    let max = -Infinity;
    for( let i = 2; i <= n; i++ ) {
        let v = -Infinity;
        if(n % i == 0) {
            v = Math.pow(n/i,i)
        } else {
            let v1 = Math.ceil(n/i),
                v2 = Math.floor(n/i)
            v = Math.max(
                Math.pow(v1,i-1) * (n - v1*(i-1)) , 
                Math.pow(v2,i-1) * (n - v2*(i-1))
            )
        }
        max = Math.max(v, max)
    }
    return max;
};
Option II:
/*
 * @lc app=leetcode.cn id=343 lang=javascript
 *
 * [343] 整数拆分
 */

// @lc code=start
/**
 * @param {number} n
 * @return {number}
 */
var integerBreak = function(n) {
    const dp = new Array(n + 1);
    dp[1] = 1;  
    dp[2] = 1; 
    for (let i = 3; i <= n; i++) {
      dp[i] = 0;
      // 对于数字 i,它可以分为两份:j 和 i-j,j 的范围是 1 到 i-j
      for (let j = 1; j <= i - j; j++) {
        // 对于 i-j 这部分可以拆或不拆,不拆就是 i-j,拆就是 dp[i-j]
        dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j]);
      }
    }
    return dp[n];
};

There are two schemes: 1. Mathematical scheme, using mean inequality for judgment and processing; 2. Dynamic programming


2021.07.09

No.410 The maximum value of the divided array

Given a non-negative integer array nums and an integer m, you need to divide this array into m non-empty continuous sub-arrays.​
Design an algorithm to minimize the maximum value of the sum of the m sub-arrays.

 

Example 1:

Input: nums = [7,2,5,10,8], m = 2
Output: 18
explain:
There are four ways to split nums into 2 sub-arrays. The best way is to divide it into [7,2,5] and [10,8].
Because the maximum value of the sum of the two sub-arrays is 18, which is the smallest in all cases.
Example 2:

Input: nums = [1,2,3,4,5], m = 2
Output: 9
Example 3:

Input: nums = [1,4,4], m = 3
Output: 4
 

hint:

1 <= nums.length <= 1000
0 <= nums[i] <= 106
1 <= m <= min(50, nums.length)

Source: LeetCode
Link: https://leetcode-cn.com/problems/split-array-largest-sum
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=410 lang=javascript
 *
 * [410] 分割数组的最大值
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @param {number} m
 * @return {number}
 */
var splitArray = function(nums, m) {
    let max = 0
        sum = 0;
    for (let i = 0; i < nums.length; i++) {
        if (max < nums[i]) max = nums[i];
        sum += nums[i];
    }

    while (max < sum) {
        let mid = parseInt((sum - max) / 2, 10) + max;
        // 随机选择的值成立则这个值默认为最大的可能结果继续查找
        if (check(nums, mid, m)) {
        sum = mid;
        } else {
        // 不满足,重置最小可能结果
        max = mid + 1;
        }
    }



    function check(nums, val, m) {
        let sum = 0,
            index = 1;
        for (let i = 0; i < nums.length; i++) {
        // 如果分段和大于了假设的结果说明,i是该分段的终点,形成一个分段
        // index记录+1,i就成了下一个分段的起点(重置sum)开始校验下一个分段
        if (sum + nums[i] > val) {
            index++;
            sum = nums[i];
        } else {
            sum += nums[i];
        }
        }
        // 如果index即分段数量满足小于等于m则说明这个假设值成立
        return index <= m;
    }

    // 返回最小可能结果
    return max;
};
Option II:
/*
 * @lc app=leetcode.cn id=410 lang=javascript
 *
 * [410] 分割数组的最大值
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @param {number} m
 * @return {number}
 */
var splitArray = function(nums, m) {
    let len = nums.length,
        sumList = Array(len + 1).fill(0),
        dp = Array.from({ length: len + 1 }, () => Array(m + 1).fill(Number.MAX_VALUE));

    // 逐位增加,反面后面根据区间求区间和
    for (let i = 0; i < len; i++) {
        sumList[i + 1] = sumList[i] + nums[i];
    }

    // 默认值
    dp[0][0] = 0;

    for (let i = 1; i <= len; i++) {
        for (let j = 1; j <= Math.min(m, i); j++) {
        // 前i个数分成j段
        for (let x = j - 1; x < i; x++) {
            // x最后一段的起点
            // perv本轮分割完成 分段中最大的和
            let prev = Math.max(dp[x][j - 1], sumList[i] - sumList[x])
            // 该分割情况下最大分段和的最小值
            dp[i][j] = Math.min(prev, dp[i][j])
        }
        }
    }

    return dp[len][m]
};

There are two schemes: 1. Dichotomy; 2. Dynamic programming


2021.07.11

No.413 Arithmetic sequence division

If a sequence has at least three elements, and the difference between any two adjacent elements is the same, the sequence is called an arithmetic sequence.​
For example, the following sequence is an arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following series are not arithmetic series.

1, 1, 2, 5, 7
 

Array A contains N numbers, and the index starts from 0. A sub-array of array A is divided into arrays (P, Q), P and Q are integers and satisfy 0<=P<Q<N.

If the following conditions are met, the sub-array (P, Q) is called an arithmetic array:

The elements A[P], A[p + 1], ..., A[Q-1], A[Q] are arithmetic. And P + 1 <Q.

The function returns the number of all sub-arrays in array A that are arithmetic arrays.

 

Example:

A = [1, 2, 3, 4]

Return: 3, there are three sub arithmetic arrays in A: [1, 2, 3], [2, 3, 4] and itself [1, 2, 3, 4].

Source: LeetCode
Link: https://leetcode-cn.com/problems/arithmetic-slices
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=413 lang=javascript
 *
 * [413] 等差数列划分
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @return {number}
 */
var numberOfArithmeticSlices = function(nums) {
    // 判断是否是等差数列
    const isArithmetic = arr => {
        const diff = arr[1] - arr[0];
        for( let p = 0, q = p+ 1; p < arr.length -1; p++, q++ ) {
            if( arr[q] - arr[p] != diff ) {
                return false;
            }
        }
        return true;
    }
    // 返回的个数
    let n = 0;

    for(let a = 0; a < nums.length - 2;a++ ) {
        for(let b = a + 2; b < nums.length; ) {
            if(!isArithmetic(nums.slice(a,b+1))) {
                break;
            } else {
                n++;
                b++
            }
        }
    }

    return n;
};
Option II:
/*
 * @lc app=leetcode.cn id=413 lang=javascript
 *
 * [413] 等差数列划分
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @return {number}
 */
var numberOfArithmeticSlices = function(nums) {
    let len = nums.length
    if (len < 3) {
        return 0
    }
    let dp = Array(len).fill(0)
    for (let i = 2;i <= len;i++) {
        if (nums[i] - nums[i - 1] === nums[i - 1] - nums[i - 2]) {
        dp[i] = dp[i - 1] + 1
        }
    }
    return dp.reduce((prev, cur) => { return prev + cur }, 0)
};

There are two schemes: 1. Violent solution; 2. Dynamic programming


2021.07.12

No.416 Divide equal and sub-sets

Give you a non-empty array nums containing only positive integers. Please judge whether you can divide this array into two subsets so that the sum of the elements of the two subsets is equal.​
 

Example 1:

Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be divided into [1, 5, 5] and [11].
Example 2:

Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be divided into two elements and equal subsets.
 

hint:

1 <= nums.length <= 200
1 <= nums[i] <= 100

Source: LeetCode
Link: https://leetcode-cn.com/problems/partition-equal-subset-sum
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=416 lang=javascript
 *
 * [416] 分割等和子集
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canPartition = function(nums) {
    if(nums.length < 2) return false;
    const sum = nums.reduce((prev, curr) => prev + curr);
    if( sum % 2 !== 0) {
        return false;
    }
    const target = sum / 2;
    nums.sort((a,b) => b-a);
    if(nums[0] > target) {
        return false;
    }
    // 动态规划
    const dp = new Array(nums.length).fill(0).map(v => new Array(target + 1, false));
    for (let i = 0; i < nums.length; i++) {
        dp[i][0] = true;
    }
    dp[0][nums[0]] = true;
    for (let i = 1; i < nums.length; i++) {
        const num = nums[i];
        for (let j = 1; j <= target; j++) {
            if (j >= num) {
                dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[nums.length - 1][target];
};

NP is complete, which can be transformed into a 0-1 knapsack problem, which can be solved by dynamic programming


2021.07.13

No.446 Arithmetic sequence split ii-subsequence

If a sequence has at least three elements, and the difference between any two adjacent elements is the same, the sequence is called an arithmetic sequence.​
For example, the following sequence is an arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following series are not arithmetic series.

1, 1, 2, 5, 7
 

Array A contains N numbers, and the index starts from 0. The array subsequence will be divided into integer sequences (P0, P1, ..., Pk), satisfying 0 ≤ P0 <P1 <... <Pk <N.

 

If the sequence A[P0], A[P1],...,A[Pk-1], A[Pk] are arithmetic, then the subsequence (P0, P1,..., PK) of the array A is called equal Difference sequence. It is worth noting that this means k ≥ 2.

The function returns the number of all arithmetic subsequences in array A.

The input contains N integers. Each integer is between -231 and 231-1, and 0 ≤ N ≤ 1000. Ensure that the output is less than 231-1.

 

Example:

Input: [2, 4, 6, 8, 10]

Output: 7

explain:
All arithmetic subsequences are:
[2,4,6]
[4,6,8]
[6,8,10]
[2,4,6,8]
[4,6,8,10]
[2,4,6,8,10]
[2,6,10]

Source: LeetCode
Link: https://leetcode-cn.com/problems/arithmetic-slices-ii-subsequence
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Program:
/*
 * @lc app=leetcode.cn id=446 lang=javascript
 *
 * [446] 等差数列划分 II - 子序列
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @return {number}
 */
var numberOfArithmeticSlices = function(nums) {
    // 定义 i 的数组
    let dp = Array(nums.length).fill(0).map(x=> new Object())
    let ans = 0
    for(let i=1; i<nums.length; ++i){
        for(let j=0; j<i; ++j){
            let sub = nums[i] - nums[j]
            // 定义 sub 的键值对 [], 第0位代表序列长度为 2,从第1位开始满足题意要求
            if(!(sub in dp[i])){
                dp[i][sub] = [0]
            }
            dp[i][sub][0] += 1
            if(sub in dp[j]){
                for(let k=0; k<dp[j][sub].length; ++k){
                    if(dp[i][sub][k+1] === undefined) dp[i][sub][k+1] = 0
                    dp[i][sub][k+1] += dp[j][sub][k]
                    ans += dp[j][sub][k]
                }
            }
        }
    }
    return ans
};

Dynamic programming


2021.07.14

No.698 is divided into k equal subsets

Given an integer array nums and a positive integer k, find out whether it is possible to divide this array into k non-empty subsets whose sums are all equal.​
Example 1:

Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
Output: True
Explanation: It is possible to divide it into 4 subsets (5), (1,4), (2,3), (2,3) equal to the sum.
 

hint:

1 <= k <= len(nums) <= 16
0 < nums[i] < 10000

Source: LeetCode
Link: https://leetcode-cn.com/problems/partition-to-k-equal-sum-subsets
The copyright belongs to Lingkou Network. For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Option One:
/*
 * @lc app=leetcode.cn id=698 lang=javascript
 *
 * [698] 划分为k个相等的子集
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var canPartitionKSubsets = function(nums, k) {
    const sum = nums.reduce((prev, curr) => prev + curr);
    if( sum % k !== 0 ) return false;
    const target = sum / k;
    nums.sort((a,b) => b - a);
    if(nums[0] > target) return false;
    // 动态规划
    const MAX_STATE = (1 << nums.length) - 1 // 1 <= N <= 16
    let dp = new Array(MAX_STATE + 1).fill(null)
    dp[0] = 0
    
    // 枚举所有状态,递推
    for (let state = 1; state <= MAX_STATE; ++state) { // O(2^N)
        for (let i = 0; i < nums.length; ++i) { // O(N)
        const iBit = 1 << i
        // 如果 state 表示未选取 nums[i] ,说明不能达到 (state, i) 状态
        if ((state & iBit) === 0) continue

        const prevState = state ^ iBit
        // NOTICE: 如果不能到达 prevState 状态
        if (dp[prevState] === null) continue
        const prevSubsetSum = dp[prevState] % target
        // 最优性剪枝优化:nums 已升序排序,如果 nums[i] 已偏大,后续更加偏大,无需尝试
        if (prevSubsetSum + nums[i] > target) break
        dp[state] = dp[prevState] + nums[i]
        }
    }

    // console.log(dp)
    return dp[MAX_STATE] === sum
};
Option II:
/*
 * @lc app=leetcode.cn id=698 lang=javascript
 *
 * [698] 划分为k个相等的子集
 */

// @lc code=start
/**
 * @param {number[]} nums
 * @param {number} k
 * @return {boolean}
 */
var canPartitionKSubsets = function(nums, k) {
    const sum = nums.reduce((prev, curr) => prev + curr);
    if( sum % k !== 0 ) return false;
    const target = sum / k;
    nums.sort((a,b) => b - a);
    if(nums[0] > target) return false;
    // 回溯
    const sums = new Array(k).fill(0);
    const helper = (i, sums, target, nums, k) => {
        if(i === nums.length) return true;
        for( let j = 0; j< k; j++ ) {
            if(sums[j] < target && nums[i] + sums[j] <= target) {
                sums[j] += nums[i];
                if(helper(i+1, sums, target, nums, k)) {
                    return true;
                }
                sums[j] -= nums[i];
            }
        }

        return false;
    }

    return helper(0,sums, target, nums, k);
};

There are two schemes: 1. Dynamic programming, which uses binary to represent the array; 2. Backtracking and recursion


2021.07.15

No.902 Number combinations up to N

We have a set of sorted numbers D, which are non-empty components of {'1','2','3','4','5','6','7','8','9'} set. (Please note that '0' is not included.)​
Now, we use these numbers in combination to write numbers, and use them as many times as we want. For example D = {'1','3','5'}, we can write numbers like '13', '551', '1351315'.

Returns the number of positive integers less than or equal to N that can be written with the numbers in D.

 

Example 1:

Input: D = ["1","3","5","7"], N = 100
Output: 20
explain:
The 20 numbers that can be written are:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.
Example 2:

Input: D = ["1","4","9"], N = 1000000000
Output: 29523
explain:
We can write 3 one-digit numbers, 9 two-digit numbers, 27 three-digit numbers,
81 four-digit numbers, 243 five-dig


维李设论
1.1k 声望4k 粉丝

专注大前端领域发展