算法-字符串-动态规划-Design

145 阅读5分钟

字符串

翻转单词151

「这个题目归到media难度让我很困惑」 给定一个字符串,逐个翻转字符串中的每个单词。 示例 1:

输入: "the sky is blue" 输出: "blue is sky the"

/**
 * @param {string} s
 * @return {string}
 */
var reverseWords = function(s) {
    let arrs = s.split(' ').filter(i => i);
    let res = '';
    for (let i = arrs.length - 1; i >= 0; i--) {
        res += arrs[i] + (i > 0 ? ' ' : '');
    }
    return res;
};
相隔为 1 的编辑距离161

给定两个字符串 s 和 t,判断他们的编辑距离是否为 1。

注意:

满足编辑距离等于 1 有三种可能的情形:

往 s 中插入一个字符得到 t 从 s 中删除一个字符得到 t 在 s 中替换一个字符得到 t 示例 1:

输入: s = "ab", t = "acb" 输出: true 解释: 可以将 'c' 插入字符串 s 来得到 t。 示例 2:

输入: s = "cab", t = "ad" 输出: false 解释: 无法通过 1 步操作使 s 变为 t。 示例 3:

输入: s = "1203", t = "1213" 输出: true 解释: 可以将字符串 s 中的 '0' 替换为 '1' 来得到 t。

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isOneEditDistance = function(s, t) {
    let difTime;
    if (Math.abs(s.length - t.length) > 1) return false;
    if (s.length > t.length) {
        let temp;
        temp = s;
        s = t;
        t = temp;
    }
    function hepler(s, t, difTime) {
        if (s.length === 0 && t.length === 1) {
            if (difTime > 0) {
                // console.log(333)
                return true;
            } else {
                return false;
            }
        }
        for (let i = 0 ; i < s.length; i++) {
            if (s[i] !== t[i]) {
                if ( difTime <= 0) {
                    // console.log(111)
                    return false;   
                } else {
                    difTime--;
                    if (s.length !== t.length) {
                        // console.log(s.substring(i), t.substring(i+1))
                        return hepler(s.substring(i), t.substring(i+1), 0)
                    }
                }
            }
        }
        if (s.length === t.length && difTime > 0) {
            // console.log(222)
            return false;
        }
        if (s.length < t.length && difTime <= 0) {
            return false;
        }
        return true;
    }
    return hepler(s, t, 1);
};
括号生成22

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例:

输入:n = 3 输出:[ "((()))", "(()())", "(())()", "()(())", "()()()" ]

/**
 * @param {number} n
 * @return {string[]}
 */
var generateParenthesis = function(n) {
    if (n === 0) return '';
    let res = [];
    function generate(left, right, s) {
        if (left === n && right === n) {
            res.push(s);
            return;
        }
        // 如果左括号没用完,加上一个左括号
        if (left < n) {
            generate(left + 1, right, s + '(');
        }
        // 如果右括号小于左括号,可以加一个右括号
        if (right < left) {
            generate(left, right + 1, s + ')');
        }
    }
    generate(0, 0, '');
    return res;
};

动态规划

不同路径63

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。

说明:m 和 n 的值均不超过 100。 输入: [ [0,0,0], [0,1,0], [0,0,0] ] 输出: 2 解释: 3x3 网格的正中间有一个障碍物。 从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右
/**
 * @param {number[][]} obstacleGrid
 * @return {number}
 */
var uniquePathsWithObstacles = function(obstacleGrid) {
    if (obstacleGrid === null ||  obstacleGrid.length == 0) {
        return 0;
    }
    let dp = [];
    let m = obstacleGrid.length, n = obstacleGrid[0].length;
    for (let i = 0; i < m; i++) {
        dp[i] = [];
        for (let j = 0; j < n; j++) {
            dp[i][j] = 0;
        }
    }
    console.log('dp', dp);
    for (let i = 0; i < m && obstacleGrid[i][0] === 0; i++) {
        dp[i][0] = 1;
    }
    for (let i = 0; i < n && obstacleGrid[0][i] === 0; i++) {
        dp[0][i] = 1;
    }
    for (let i = 1; i < m ; i++) {
        for (let j = 1; j < n; j++) {
            if (obstacleGrid[i][j] == 0) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    }
    return dp[m - 1][n - 1];
};

Design

LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得关键字 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得关键字 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

class ListNode {
  constructor(key, value) {
    this.key = key
    this.value = value
    this.next = null
    this.prev = null
  }
}

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity
    this.hashTable = {}
    this.count = 0
    this.dummyHead = new ListNode()
    this.dummyTail = new ListNode()
    this.dummyHead.next = this.dummyTail
    this.dummyTail.prev = this.dummyHead
  }

  get(key) {
    let node = this.hashTable[key]
    if (node == null) return -1
    this.moveToHead(node)
    return node.value
  }

  put(key, value) {
    let node = this.hashTable[key]
    if (node == null) {
      let newNode = new ListNode(key, value)
      this.hashTable[key] = newNode
      this.addToHead(newNode)
      this.count++
      if (this.count > this.capacity) {
        this.removeLRUItem()
      }
    } else {
      node.value = value
      this.moveToHead(node)
    }
  }

  moveToHead(node) {
    this.removeFromList(node)
    this.addToHead(node)
  }
  
  removeFromList(node) {
    let tempForPrev = node.prev
    let tempForNext = node.next
    tempForPrev.next = tempForNext
    tempForNext.prev = tempForPrev
  }

  addToHead(node) {
    node.prev = this.dummyHead
    node.next = this.dummyHead.next
    this.dummyHead.next.prev = node
    this.dummyHead.next = node
  }

  removeLRUItem() {
    let tail = this.popTail()
    delete this.hashTable[tail.key]
    this.count--
  }

  popTail() {
    let tailItem = this.dummyTail.prev
    this.removeFromList(tailItem)
    return tailItem
  }
}
常数时间插入、删除和随机获取元素 380

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

insert(val):当元素 val 不存在时,向集合中插入该项。 remove(val):元素 val 存在时,从集合中移除该项。 getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。 示例 :

// 初始化一个空的集合。 RandomizedSet randomSet = new RandomizedSet();

// 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomSet.insert(1);

// 返回 false ,表示集合中不存在 2 。 randomSet.remove(2);

// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomSet.insert(2);

// getRandom 应随机返回 1 或 2 。 randomSet.getRandom();

// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomSet.remove(1);

// 2 已在集合中,所以返回 false 。 randomSet.insert(2);

// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。 randomSet.getRandom();

/**
 * Initialize your data structure here.
 */
var RandomizedSet = function() {
    this.hashTable = {};
    this.arrayTable = [];
};

/**
 * Inserts a value to the set. Returns true if the set did not already contain the specified element. 
 * @param {number} val
 * @return {boolean}
 */
RandomizedSet.prototype.insert = function(val) {
    if (this.hashTable.hasOwnProperty(val)) {
        return false;
    }
    this.hashTable[val] = this.arrayTable.length;
    this.arrayTable.push(val);
    return true;
};

/**
 * Removes a value from the set. Returns true if the set contained the specified element. 
 * @param {number} val
 * @return {boolean}
 */
RandomizedSet.prototype.remove = function(val) {
    if (this.hashTable.hasOwnProperty(val)) {
        if (this.hashTable[val] !== this.arrayTable.length - 1) {
            const index = this.hashTable[val];
            const lastVal = this.arrayTable[this.arrayTable.length - 1];
            this.arrayTable[index] = lastVal;
            this.hashTable[lastVal] = index;
        }
        this.arrayTable.splice(this.arrayTable.length - 1, 1);
        delete this.hashTable[val];
        return true;
    }
    return false;
};

/**
 * Get a random element from the set.
 * @return {number}
 */
RandomizedSet.prototype.getRandom = function() {
    const length = this.arrayTable.length;
    const randomKey = Math.floor(Math.random() * length);
    return this.arrayTable[randomKey];
};