zoukankan      html  css  js  c++  java
  • leetcode每日一题(2020-07-09):面试题 17.13. 恢复空格

    题目描述:
    哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。
    注意:本题相对原题稍作改动,只需返回未识别的字符数

    今日学习:
    1.复习reduce
    2.动规动规又是动规我哭了
    3.字典树的概念
    4.对象动态属性赋值obj[name]

    题解:
    1.错误原因:有时候不一定全用较长的单词剩下的字符少,有可能短单词会更紧凑剩的更少
    2.错误原因:理解错题意了
    3.动规
    4.错误原因:同1
    5.字典树

    /**
     * @param {string[]} dictionary
     * @param {string} sentence
     * @return {number}
     */
    //思路一:
    //1.在句子中遍历字典里的单词,查找单词在句子中出现的次数,记录到times中
    var respace = function(dictionary, sentence) {
        let slen = sentence.length
        let dlen = dictionary.length
        if(slen == 0) return 0
        dictionary.sort((a, b) => b.length - a.length)
        //字典中每个单词出现的次数
        let times = new Array(dlen).fill(0)
        //字典中每个单词的长度
        let lens = new Array(dlen)
        for(let i = 0; i < dlen; i++) {
            lens[i] = dictionary[i].length
        }
        for(let i = 1; i <= slen; i++){
            //每次比较时候用到的temp次数,按理说每一次i,temp里只能出现一个1
            let temp = new Array(dlen).fill(0)
            for(let j = 0; j < dlen; j++){
                let word = lens[j]
                if(dictionary[j] == sentence.substring(i - word, i) && word <= i){
                    temp[j]++ 
                }
            }
            let index
            if(temp.reduce((total,item) => total + item) > 1) {
                index = temp.indexOf(1)
                for(let k = index + 1; k < dlen; k++) {
                    if(temp[k] == 1) {
                        index = lens[k] > lens[index] ? k : index
                    }
                }
                times[index]++
            }else {
                index = temp.indexOf(1)
                if(index != -1) {
                    times[index]++
                }
            }
        }
        for(let i = 0; i < dlen; i++) {
            slen -= lens[i] * times[i]
        }
        return slen
    };
    //思路二:
    //1.遍历s,查找是否有字典内的字符串,不在开始位置就数量+2,在起始位置就+1
    //【理解错题意了,jesstim一共七个字符,所以输出7,而不是7个单词】
    //2.截取后面的s,继续查找
    /*var respace = function(dictionary, sentence) {
        for(let i = 0; i < sentence.length; i++) {
            for(let j = i + 1; j < sentence.length; j++) {
                 let s = sentence.slice(i, j)
            }
        }
    }*/
    //思路三:
    //动规
    var respace = function (dictionary, sentence) {
        let len = sentence.length
        if(len == 0) return 0
        let dp = new Array(len + 1).fill(0)
        for(let i = 1; i <= len; i++){
            // 先假设加进来的字符没有组成新词,那么dp[i]相比于dp[i-1]直接多1
            dp[i] = dp[i - 1] + 1
            // 接着讨论如果新加进来的字符和之前组成了一个词的情况
            // 与字典中的每一个字符串比较看是否组成了新词
            for(let j = 0; j < dictionary.length; j++){
                let word = dictionary[j].length
                // 如果组成了新词,更新dp,按最小的未识别算
                // 因为像her,brother这种,明显使用brother使得未识别字符数最少
                if(dictionary[j] == sentence.substring(i - word, i) && word <= i){
                    dp[i] = Math.min(dp[i], dp[i - word])
                }
            }
        }
        return dp[len]
    }
    //思路四:正则匹配+替换
    const respace = (dictionary, sentence) => {
      dictionary.sort((a, b) => b.length - a.length); // 由大到小进行排序
      let count = 0
      for (let i = 0; i < dictionary.length; i++) {
        sentence = sentence.replace(new RegExp(dictionary[i], 'g'), ' ');
      }
      for(let i = 0; i < sentence.length; i++) {
          if(sentence[i] == ' ')
          count++
      }
      return sentence.length - count;
    // return sentence
    };
    //思路五:字典树
    /**
     * @param {string[]} dictionary
     * @param {string} sentence
     * @return {number}
     */
    var respace = function (dictionary, sentence) {
      var len = sentence.length;
      if (!len) return 0;
    
      // 生成字典树
      const trie = {};
      for (let i = 0, l = dictionary.length; i < l; i++) {
        const word = dictionary[i];
        let node = trie;
        for (let j = 0, m = word.length; j < m; j++) {
          const char = word[j];
          node = node[char] || (node[char] = {});
        }
        // 标记单词结束
        node['$'] = true;
      }
    
      const cache = new Array(len).fill(Infinity);
      let res = Infinity;
    
      // DFS
      const stack = [0];
      const countStack = [0];
      while (stack.length) {
        // 当前单词匹配起点
        const index = stack.pop();
        // 当前未识别字符数
        const count = countStack.pop();
        // 从开头到前一个字符的子串,未识别字符数的最小值
        const n = cache[index];
    
        // console.log(`已匹配子串 ${sentence.slice(0, index)}, 未识别字符数为 ${count}`);
    
        // 未识别字符数不小于当前最小值,放弃该分支
        if (count >= n || count >= res) continue;
    
        // 已完成整个字符串的匹配
        if (index >= len) {
          // console.log(`■■■■■ 匹配完成,未识别字符数为 ${count} ■■■■■`);
          if (count < res) res = count;
          continue;
        }
    
        // 进行单词匹配
        let i = index;
        let node = trie[sentence[i++]];
    
        // 如果匹配到一个字符的单词,选择该单词的分支为更优解,放弃未识别的分支
        if (!(node && node['$'])) {
          // 添加当前字符为未识别字符的分支
          stack.push(index + 1);
          countStack.push(count + 1);
        }
    
        // 寻找可识别的单词
        while (node) {
          // 成功识别到单词,添加分支
          if (node['$']) {
            // console.log(`识别到单词 ${sentence.slice(index, i)}`);
            stack.push(i);
            countStack.push(count);
          }
          node = node[sentence[i++]];
        }
    
        if (count < n) cache[index] = count;
      }
    
      return res;
    };
    
  • 相关阅读:
    Vue实现上下整屏滚动效果
    Vue实现选项卡切换效果
    Vue中在main.js中引入字体图标问题
    Vue中的生命周期beforeDestory不触发问题
    Vue中解决移动端点击300毫秒延迟的问题
    Vue中全局和按需引入Echarts
    Vue组件间的通信
    Echarts图表属性设置
    JZOJ 6799. 【2014广州市选day2】game
    JZOJ 6798. 【2014广州市选day2】regions
  • 原文地址:https://www.cnblogs.com/autumn-starrysky/p/13273000.html
Copyright © 2011-2022 走看看