LeetCode9月 每日一题

Posted woodwhale

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode9月 每日一题相关的知识,希望对你有一定的参考价值。

【LeetCode】9月 每日一题

9.1

题目

1475. 商品折扣后的最终价格

思路

模拟即可

代码

function finalPrices(prices: number[]): number[] 
    let len: number = prices.length
    let res: number[] = new Array(len)
    for (let i = 0; i < len - 1; i++) 
        let discount: number = 0
        let price: number = prices[i]
        for (let j = i + 1; j < len; j++) 
            if (price >= prices[j]) 
                discount =prices[j]
                break
            
        
        res[i] = price - discount
    
    res[len - 1] = prices[len - 1]
    return res

9.2

题目

687. 最长同值路径

思路

dfs即可,但是是在dfs的过程中找到最大值,dfs的返回值是左右选择后的最长路径,最终需要返回的结果是左右所有的路径加起来

代码

/**
 * Definition for a binary tree node.
 * class TreeNode 
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) 
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     
 * 
 */

function longestUnivaluePath(root: TreeNode | null): number 
    let res = 0
    const find = (node: TreeNode) => 
        if (node == null) return 0
        let l = find(node.left), r = find(node.right)
        let al = 0, ar = 0
        if (node.left && node.left.val == node.val)  al = l + 1
        if (node.right && node.right.val == node.val) ar = r +  1
        res = Math.max(res, al + ar)
        return Math.max(al, ar)
    
    find(root)
    return res

9.3

题目

646. 最长数对链

思路

dp思想,先排序,按照头一个元素升序,如果头一个元素相同,就按照后一个减去前一个的顺序

然后就是dp[i]表示0到1的最长数对链的长度

每次遍历选取当前遍历坐标前面的最长的长度,如果当前这个可以加入链中,dp[i]+1

代码

function findLongestChain(pairs: number[][]): number 
    let len = pairs.length
    if (len == 1) return 1
    pairs.sort((a,b) => a[0] == b[0] ? b[1] - a[0] : a[0] - b[0])
    let dp = new Array(len).fill(0)
    dp[0] = 1
    let res = -Infinity
    for (let i = 1; i < len; i++) 
        let tmp = 0
        for (let j = 0; j < i; j++) 
            if (pairs[i][0] > pairs[j][1] && pairs[i][1] > pairs[j][1]) 
                tmp = Math.max(dp[j], tmp)
            
            dp[i] = tmp + 1
            res = Math.max(res, dp[i])
        
    
    return res

9.4

题目

1582. 二进制矩阵中的特殊位置

思路

暴力梭哈遍历

代码

function numSpecial(mat: number[][]): number 
    const rows: number = mat.length
    const cols: number = mat[0].length
    let res: number = 0
    const isSpecial: Function = (x: number, y: number): boolean => 
        if (mat[x][y] == 0) return false
        for (let col = 0; col < cols; col++) 
            if (col == y) continue
            if (mat[x][col] != 0) return false
        
        for (let row = 0; row < rows; row++) 
            if (row == x) continue
            if (mat[row][y] != 0) return false
        
        return true
    
    for (let i = 0; i < rows; i++) 
        for (let j = 0; j < cols; j++) 
            if (isSpecial(i, j)) res++
        
    
    return res

9.5

题目

652. 寻找重复的子树

思路

众所周知我不会dfs,只能用bfs混混日子这样子(主要是脑容量太小了无法用程序的逻辑在脑子里模拟一个个stack,只能用que的空间换脑容量

这题其实思路很简单,hash+serialization,记录每次序列化后的结果,比较是否出现过,出现过就加入最后的返回结果中

但是因为我是使用bfs,第一次直接malloc空间不足了,给我整不会了

后来想明白了,bfs必然过不了,向左右扩展肯定是会挂的,因为每次都会重复计算大量相同的数据

还是得回归dfs,挖到最deep的一层,从下向上serialization,这样就不会重复计算,下方计算好的结果str可以交给上方重复利用

代码

第一版wa的bfs代码,内存不够用

/**
 * Definition for a binary tree node.
 * class TreeNode 
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) 
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     
 * 
 */

type Tree = TreeNode | null
function findDuplicateSubtrees(root: TreeNode | null): Array<TreeNode | null> 
    // 前序遍历进行序列化
    const frontOrder = (node: TreeNode) => 
        let res: string = ""
        let stack: Tree[] = [node]
        while (stack.length) 
            let cur: Tree = stack.pop()
            if (cur == null) res += "#"
            else res += cur.val
            res += ','
            if (cur != null) 
                stack.push(cur.right)
                stack.push(cur.left)
            
        
        return res
    
    let stack: Tree[] = [root]
    let map: Map<string, number> = new Map()
    let res: Tree[] = []
    // 前序遍历对比序列化结果
    while (stack.length) 
        let node: Tree = stack.pop()
        let serial = frontOrder(node)
        // console.log(set, serial)
        if (map.get(serial) == undefined) 
            map.set(serial, 1)
         else if (map.get(serial) == 1)  // 出现过一次就加入结果
            res.push(node)
            map.set(serial, map.get(serial) + 1)
         else 
            map.set(serial, map.get(serial) + 1)
        
        if (node.right) stack.push(node.right)
        if (node.left) stack.push(node.left) 
    
    return res

执行执行几个运行示例是完全没压力的,看了一眼数据范围,10的四次方,必然超内存

于是使用dfs的版本来了

/**
 * Definition for a binary tree node.
 * class TreeNode 
 *     val: number
 *     left: TreeNode | null
 *     right: TreeNode | null
 *     constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) 
 *         this.val = (val===undefined ? 0 : val)
 *         this.left = (left===undefined ? null : left)
 *         this.right = (right===undefined ? null : right)
 *     
 * 
 */

type Tree = TreeNode | null
function findDuplicateSubtrees(root: TreeNode | null): Array<TreeNode | null> 
    const map: Map<string, number> = new Map() // 记录出现的序列化的结果的次数
    const res: Tree[] = []
    const frontOrder = (node: Tree) => 
        if (node == null) return "#"
        let serializtion = node.val + "," + frontOrder(node.left) + "," +frontOrder(node.right)
        if (map.get(serializtion) == undefined) 
            map.set(serializtion, 1)
         else if (map.get(serializtion) == 1) 
            res.push(node)
            map.set(serializtion, 2)
         else 
            map.set(serializtion, map.get(serializtion) + 1)
        
        return serializtion
    
    frontOrder(root)
    return res

这题真的思路一下子打开了,以前总以为dfs和bfs在实际应用可以互相转换,但似乎这题不用dfs真滴很难使用bfs来进行树的向下模拟

顺带一提,使用c++版本的dfs还是可以勉强过的,估计是语言特性,typescript这种封装类型的语言就是比不过原生类型的,为了这题我甚至专门写了一个题解

9.6

题目

828. 统计子串中的唯一字符

思路

典中典奥数题,还是得看题解

对每一个字符i,向前找到相同的字符j,向后找到相同的字符k。当前字符对最终结果的贡献是:(i-j)*(k-i)。

这相当于两种方案的拼接:在字符串A(j到i)当中,字符i贡献的次数是(i-j)次。在字符串B(k-i)当中,字符i贡献的次数是(k-i)。那么当两者拼接的时候,字符i对子串(j到k)的贡献就是两种方案的乘积(符合乘法公式)。

遍历两次,分别得到每个字符最左边出现和最右边出现的位置,结果两个距离相乘

代码

function uniqueLetterString(s: string): number 
    const len: number = s.length
    const l: number[] = new Array(len)
    const r: number[] = new Array(len)
    const cnts: number[] = new Array(26).fill(-1)
    for (let i = 0; i < len; i++) 
        let tmp: number = s[i].charCodeAt(0) - 'A'.charCodeAt(0)
        l[i] = cnts[tmp]
        cnts[tmp] = i
    
    cnts.fill(len)
    for (let i = len - 1; i >= 0; i--) 
        let tmp: number = s[i].charCodeAt(0) - 'A'.charCodeAt(0)
        r[i] = cnts[tmp]
        cnts[tmp] = i
    
    let res: number = 0
    for (let i = 0; i < len; i++) 
        console.log(l[i], i, r[i])
        res += (i - l[i]) * (r[i] - i)
    
    return res

9.7

题目

1592. 重新排列单词间的空格

思路

纯纯的模拟,有啥好思路

好思路就是我用python

代码

class Solution:
    def reorderSpaces(self, text: str) -> str:
        space_cnt = 0
        words = []
        word = ''
        for i in range(len(text)):
            if text[i] == ' ':
                space_cnt += 1
                if word != '':
                    words.append(word)
                    word = ''
            else:
                word += text[i]
            if i == len(text) - 1 and word != '':
                words.append(word)
        words_cnt = len(words)
        if (words_cnt == 1):
            return words[0] + " " * space_cnt
        average_spaec_cnt = space_cnt // (words_cnt - 1)
        ramain_space_cnt = space_cnt  % (words_cnt - 1)
        res = (" " * average_spaec_cnt).join(words) + " " * ramain_space_cnt
        return res

9.8

题目

667. 优美的排列 II

思路

由于最近回溯写得多,以看这种题目又以为是回溯,直接全排列的dfs+剪枝,以看数据范围1e4,直接wa

最后还是得去找规律

当n = 50, k = 20时:
[1,21,2,20,3,19,4,18,5,17,6,16,7,15,8,14,9,13,10,12,11,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
当n = 50,k = 17时:
[1,18,2,17,3,16,4,15,5,14,6,13,7,12,8,11,9,10,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50]
当n = 80,k = 30时:
[1,31,2,30,3,29,4,28,5,27,6,26,7,25,8,24,9,23,10,22,11,21,12,20,13,19,14,18,15,17,16,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80]
是不是发现了规律,就是
下标从[0, k]中,偶数下标填充[1,2,3…],奇数下标填充[k + 1, k, k - 1…],后面[k + 1, n - 1]都是顺序填充

代码

失败的dfs

function constructArray(n: number, k: number): number[] 
    // n个数
    const used = new Array(n+1).fill(false)
    used[0] = true
    let res: number[]
    const set: Set<number> = new Set()
    const dfs = (num: number, arr: number[]) => 
        if (num > n && set.size == k+1 && arr.length == n) 
            console.log(arr, set, num)
            res = [...arr]
            return true
        
        for (let i = 1; i <= n; i++) 
            const sub = Math.abs(arr[arr.length - 1] - i)
            if (!used[i]) 
                set.add(sub)
                used[i] = true
                arr.push(i)
                if (dfs(i+1, arr)) return true
                set.delete(sub)
                used[i] = false
                arr.pop()
            
        
        return false
    
    dfs(1, [])
    return res

找规律

function constructArray(n: number, k: number): number[] 
    const res: number[] = new Array(n)
    let tmp: number = 1, retmp:以上是关于LeetCode9月 每日一题的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode9月 每日一题

每日一题1464. 数组中两元素的最大乘积

Leetcode 152每日一题:乘积最大子数组

《LeetCode之每日一题》:210.最大单词长度乘积

每日一题1800. 最大升序子数组和

每日一题1800. 最大升序子数组和