LeetcodeInJS--String(持續更新)

709 toLowerCase

  • 難度:easy
  • 標籤:ASCII碼
  • 初始思路:大寫字母ASCII範圍65-90,小寫字母ASCII範圍97-122,func_大寫轉小寫即爲val+32git

    resultStr = ''
    for(str) {
        if (str[i] in 大寫字母ASCII碼範圍) {
            resultStr + = func_大寫轉小寫(str[i])
        } else {
            resultStr += str[i]
        }
    }
    return resultStr
  • 複雜度:時間 O(N), 空間 O(1)
  • 優化:
    第一次優化:使用正則判斷字符是否處於大寫字母ASCII碼範圍,只有處於該範圍內才進行進行轉ASCII處理,結果複雜度不變,減小了轉換ASCII碼的次數。實現以下:數組

    var toLowerCase = function(str) {
        let resultStr = '';
        for (let i=0, strLen=str.length; i<strLen; i++) {
            let tempChar = str[i];
            resultStr += /[A-Z]/.test(tempChar)? 
                String.fromCharCode(tempChar.charCodeAt()+32)
                : tempChar;
        }
        return resultStr;
    };

804 uniqueMorseRepresentations

  • 難度:easy
  • 初始思路:使用Set存儲計算結果
  • 複雜度: 時間:雙for=>O(n^2), 空間:最差狀況即所有字符串Morse碼不一樣時爲O(n)
  • 實現:函數

    var uniqueMorseRepresentations = function(words) {
        let morseArr = [".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."];
        let set = new Set();
        for (let i=0, wordsLen=words.length; i<wordsLen; i++) {
            let tempMorseStr = '';
            for (let j=0, word=words[i]; j<word.length; j++) {
                tempMorseStr += morseArr[word[j].charCodeAt() - 97];
            }
            set.add(tempMorseStr);
        }
        return set.size;
    };

929 numUniqueEmails

  • 難度:easy
  • 標籤:Set 正則
  • 題意分析:只需關注@前面部分,遇.去掉,遇+忽略其後字符;
  • 初始思路:基於@分割,前面部分正則去點,而後取加號以前的部分,組合放入set去重;優化

    • 複雜度:時間O(n),空間O(n)
    • 實現ui

      var numUniqueEmails = function(emails) {
           let set = new Set();
           for (let i=0, emailsLen=emails.length; i<emailsLen; i++) {
               let tempArr = emails[i].split('@');
               let localStr = tempArr[0];
               localStr = localStr.replace(/\./g, '');
               let indexAdd = localStr.indexOf('+');
               if (indexAdd>-1) { localStr=localStr.slice(0, indexAdd); }
               set.add(localStr+'@'+tempArr[1]);
           }
           return set.size;
       };
  • 首次優化:先基於加號分割再正則去點,組合操做一塊兒提升可讀性;spa

    • 實現:code

      var numUniqueEmails = function(emails) {
          let set = new Set();
          for (let i=0, emailsLen=emails.length; i<emailsLen; i++) {
              let tempArr = emails[i].split('@');
              let localStr = tempArr[0].split('+')[0].replace(/\./g, '');
              set.add(localStr+'@'+tempArr[1]);
          }
          return set.size;
      };

22 generateParenthesis

  • 難度: medium
  • 標籤:遞歸
  • 初始思路:建立校驗函數,生成全部狀況的組合後逐個校驗;對象

    • 複雜度:時間、空間都 N!

圖片描述

  • 優化:分析正確組合結果生成規律,只生成符合要求的結果;blog

    • 左右括號規則(每次新增都有添加左括號和添加右括號兩種選擇,故重點在於瞭解不得添加狀況):排序

      1 . 不可加左括號:左括號數量===Num
      2 . 不可加右括號:首位、左右括號數量相等時
    • 思路:用函數不斷根據左右括號規則運行添加,最終生成目標結果;
    • 複雜度:時間O(n)、空間O(n)
    • 實現:

      var generateParenthesis = function(n) {
          let arr = [];
          if (n===0) return [];
          calcFunc(arr, n, 0, 0, '');  
          return arr;
      };
      
      function calcFunc(resultArr, N, leftNum, rightNum, currStr) {
          if (leftNum+rightNum === N*2) {
              resultArr.push(currStr);
              return;
          }
          if (leftNum !== N) {
              calcFunc(resultArr, N, leftNum+1, rightNum, currStr+'(');
          }
          if (currStr !== '' && leftNum !== rightNum) {
              calcFunc(resultArr, N, leftNum, rightNum+1, currStr+')');
          }
      }

657 judgeCircle

  • 難度:easy
  • 初始思路:放置兩個計數器,for字符串並增減計數器,最終計數器歸0則True;

    • 複雜度:時間O(n) 空間O(1)
    • 實現:

      var judgeCircle = function(moves) {
        let [x, y] = [0, 0];
        for (let i=0, movesLen=moves.length; i<movesLen; i++) {
            if (moves[i] === 'L') {
                x++;
            } else if (moves[i] === 'R') {
                x--;       
            } else if (moves[i] === 'U') { 
                y++; 
            } else if (moves[i] === 'D') {
                y--;
            }
        }
        return (x===0 && y===0)? true: false;
      };
  • 思路二:使用Hashmap作須要字符數量的存儲,及最後用以對比

    • 複雜度:時間O(n) 空間O(1)
    • 實現:
    let map = new Map();
    map.set('U', 0);
    map.set('D', 0);
    map.set('L', 0);
    map.set('R', 0);
    for (let i=0, movesLen=moves.length; i<movesLen; i++) {
        map.has(moves[i])? map.set(moves[i], map.get(moves[i])+1): '';
    }
    return map.get('U')===map.get('D') && map.get('L')===map.get('R')

344 reverseString

  • 難度:easy
  • 題意分析:原地翻轉數組並輸出,空間複雜度需爲O(1)
  • 思路:首尾ij向中間推動並交換,i<j判斷失敗則退出

    • 複雜度:時間O(n), 空間O(1)
    • 實現:
    var reverseString = function(s) {
        let [i, j] = [0, s.length-1];
        while (i<j) {
            [s[i], s[j]] = [s[j], s[i]]
            i++
            j--
        }
        return s
    };

890 findAndReplacePattern

  • 難度:medium
  • 題意分析:word和pattern的每一個字母能構成不重複映射即知足條件
  • 初始思路:for words, 再for pattern.length, 當map不存在當前字母則添加,當map存在當前字母時比對,成功繼續,失敗next word

    • Tip:隱蔽規則:「沒有兩個字母映射到同一個字母」, 即字母列表&對應pattern列表長度應始終一致(藉助set與map長度對比)
    • 複雜度:時間O(n),空間O(n)
    • 實現:
    var findAndReplacePattern = function(words, pattern) {
        let resultArr = [];
        let patternLen = pattern.length;
        for(let i=0, wordsLen=words.length; i<wordsLen; i++) {
            let map = new Map();
            let setVal = new Set();
            let word = words[i];
            let flag = true;
            for (let j=0; j<patternLen; j++) {
                if (map.has(pattern[j])) {
                    if (map.get(pattern[j]) !== word[j]) {
                        flag = false;
                        break;
                    }
                } else {
                    map.set(pattern[j], word[j]);
                    setVal.add(word[j]);
                    if ( map.size!== setVal.size) {
                        flag = false;
                        break;
                    }
                }
            }
            if (flag) { resultArr.push(word); }
        }
        return resultArr;
    };
    • 圍觀:

      • 排名第一:遍歷pattern用pattern.indexOf(item)獲取下標數組,使words也按照這個方法對比;
      • 其餘:大同小異封裝方法,只封裝一次用以檢測足矣;
    • 總結:本題實際上是:"如何制定對比word和pattern的規則",注意點是一一映射

557 reverseWords

  • 難度:easy
  • 題意分析:"注意"中提到,"每一個單詞由單個空格分隔,且字符串中不會有任何額外的空格",因而解題只須要先基於單個空格做分割,而後依次反轉每一個單詞就行(時間複雜度:O(n^2),空間複雜度:O(n^2))
  • 實現:

    var reverseWords = function(s) {
        let resultS = ''
        let arr = s.split(' ')
        for (let i=0, arrLen=arr.length; i<arrLen; i++) {
            resultS += arr[i].split('').reverse().join('') + ' '
        }
        return resultS.trim()
    };
  • 思路二:遍歷字符串並處理每段單詞(記錄開始位,遇到【下位爲空格or最後一位】記錄結束位&處理,處理完成後記錄結束位+2爲起始位),時間空間複雜度不變,減小了split('').reverse().join('')形成的空間損耗,實現以下:

    var reverseWords = function(s) {
        let arr = s.split('')
        let [startIndex, endIndex] = [0, 0]
        for (let i=0, arrLen=arr.length; i<arrLen; i++) {
            if (arr[i+1]===' ' || i===arrLen-1) {
                endIndex = i
                reserveArr(arr, startIndex, endIndex)
                startIndex = i + 2
            }
        }
        return arr.join('')
    };
    
    function reserveArr (targetArr, sIndex, eIndex) {
        // console.log('[sIndex, eIndex]:', [sIndex, eIndex])
        while (sIndex < eIndex) {
            [targetArr[sIndex], targetArr[eIndex]] = [targetArr[eIndex], targetArr[sIndex]]
            sIndex++
            eIndex--
        }
    }

537 complexNumberMultiply

  • 難度:medium
  • 題意分析:根據給定的兩個格式爲a+bi的複數字符串,計算出a+bi格式的結果字符串
  • 思路:使用字符串分割或者正則提取輸入字符串的a和b值,計算得出結果a和b值,填充入模板字符串並返回

    • 複雜度:時空均O(1)
    • 實現:

      var complexNumberMultiply = function(a, b) {
          let [aArr, bArr] = [a.split('+'), b.split('+')]
          let [a1, b1] = [aArr[0], aArr[1].split('i')[0]]
          let [a2, b2] = [bArr[0], bArr[1].split('i')[0]]
          let [aResult, bResult] = [a1*a2-b1*b2, a1*b2+a2*b1]
          return `${aResult}+${bResult}i`
      }

521 findLUSlength

  • 難度:easy
  • 題意分析:這道題着重題意分析,目標是獲取最長特殊序列(定義:獨有的最長子序列)。可得兩字符串不相同時一定不互爲子序列,故取長者返回;若相等則互爲子序列而非最長特殊序列,即不存在,返回-1
  • 實現:

    var findLUSlength = function(a, b) {
        if (a === b) { 
            return -1 
        } else {
            return Math.max(a.length, b.length)
        }
    };

791 customSortString

  • 難度:medium
  • 題意解析:使T按照S的順序作排列,S中不存在的字符可隨意排列
  • 思路一:暴力思路(不推薦)。切分S造成順序數組,並以此造成char+count的Map。for T並加入Map,for SArr按序造成結果字符串

    • 特色:思路簡單,空間佔用多,代碼繁瑣
    • 複雜度:時間O(T), 空間O(S+2T)
    • 實現:

      var customSortString = function(S, T) {
          let orderArr = S.split('')
          let countMap = new Map()
          let resultS = ''
          for (let i=0, SLen=S.length; i<SLen; i++) {
              countMap.set(S[i], 0)
          }
          for (let i=0, TLen=T.length; i<TLen; i++) {
              if (countMap.has(T[i])) {
                  countMap.set(T[i], countMap.get(T[i])+1)    
              } else {
                  countMap.set(T[i], 1)
                  orderArr.push(T[i])
              }
          }
          for (let i=0, orderArrLen=orderArr.length; i<orderArrLen; i++) {
              for (let j=0; j<countMap.get(orderArr[i]); j++) {
                  resultS += orderArr[i]
              }
          }
          return resultS
      };
  • 思路二:用Map存儲S順序,而後用數組存儲每一個S位置所對應的全部T字符,整合輸出

    • 特色:思路&代碼清晰
    • 實現:

      var customSortString = function(S, T) {
          let SLen = S.length
          let map = new Map()
          let resultArr = Array.from({length: SLen+1}, ()=>'')
          for (let i=0; i<SLen; i++) {
              map.set(S[i], i)
          }
          for (let i=0, TLen=T.length; i<TLen; i++) {
              if (map.has(T[i])) {
                  resultArr[map.get(T[i])] += T[i]
              } else {
                  resultArr[SLen] += T[i]
              }
          }
          return resultArr.join('')
      };

791 numSpecialEquivGroups

  • 難度:easy
  • 題意分析:目標是將數組內特殊等價的字符串概括爲一組並求總數組長度。判斷是否爲特殊等價的依據是,奇位字符相同&偶位字符相同(忽略順序)。
  • 思路:for數組,取得item並將其分開爲奇字符串及偶字符串,sort兩個字符串並整合放入Set中,Set長度即結果。

    • 實現

      var numSpecialEquivGroups = function(A) {
        let set = new Set();
        let itemSize = A[0].length;
        for (let i=0, ALen=A.length; i<ALen; i++) {
          let [oArr, eArr] = [[], []];
          for (let j=0; j<itemSize; j++) {
            if (j%2===0) { eArr.push(A[i][j]) }
            else { oArr.push(A[i][j]) }
          }
          set.add(oArr.sort().join('')+eArr.sort().join(''));
        }
        return set.size;
      };

12. intToRoman

  • 難度:medium
  • 題意分析:將一個0~3999的數轉換爲羅馬數字
  • 初始思路:枚舉0~9(間隔1)、10-90(間隔10)、100~900(間隔100)、1000-3000(間隔1000), 而後循環取數字最後一位取得對應字符串,累加結果。

    • 實現:

      var intToRoman = function(num) {
          let fixedArr = [
              ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'],
              ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
              ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
              ['', 'M', 'MM', 'MMM']
          ];
          let resultStr = '';
          let count = 0;
          while (num!==0) {
              resultStr = fixedArr[count++][num%10] + resultStr
              num = Math.floor(num/10)
          }
          return resultStr;
      };
  • 優化思路:直接一路暴力if下來,免去了預先定義數組及從二維數組中取值,故速度空間都獲得節省。

    • 實現:

      var intToRoman = function(num) {
          let resultStr = ''
          if (num>=1000) {
              for (let i=0, len=Math.floor(num/1000); i<len; i++) {
                  resultStr += 'M'
              }
              num = num%1000
          }
          if (num>=900) {
              resultStr += 'CM'
              num -= 900
          }
          if (num>=500) {
              resultStr += 'D'
              num -= 500
          }
          if (num>=400) {
              resultStr += 'CD'
              num -= 400
          }
          if (num>=100) {
              for (let i=0, len=Math.floor(num/100); i<len; i++) {
                  resultStr += 'C'
              }
              num = num%100
          }
          if (num>=90) {
              resultStr += 'XC'
              num -= 90
          }
          if (num>=50) {
              resultStr += 'L'
              num -= 50
          }
          if (num>=40) {
              resultStr += 'XL'
              num -= 40
          }
          if (num>=10) {
              for (let i=0, len=Math.floor(num/10); i<len; i++) {
                  resultStr += 'X'
              }
              num = num%10
          }
          if (num>=9) {
              resultStr += 'IX'
              num -= 9
          }
          if (num>=5) {
              resultStr += 'V'
              num -= 5
          }
          if (num>=4) {
              resultStr += 'IV'
              num -= 4
          }
          if (num>=1) {
              for (let i=0; i<num; i++) {
                  resultStr += 'I'
              }
          }
          return resultStr;
      };

13. romanToInt

  • 難度:easy
  • 題意分析:將羅馬數字轉換爲數字
  • 思路:建立對象映射單個羅馬數字與數值的關係,遍歷羅馬數字字符串,比較第 i 位與第 i+1 位的值,若是第 i 位的值小於第 i+1 位,則額外計算,其餘狀況直接計算得到結果。

    • 實現:

      var romanToInt = function(s) {
          let fixedObj = {
              '': 0,
              'I': 1,
              'V': 5,
              'X': 10,
              'L': 50,
              'C': 100,
              'D': 500,
              'M': 1000
          };
          let result = 0;
          for (let i=0; i<s.length; i++) {
              if (s[i+1] && fixedObj[s[i+1]]>fixedObj[s[i]]) {
                  result += fixedObj[s[i+1]] - fixedObj[s[i++]];
              } else {
                  result += fixedObj[s[i]];
              }
          }
          return result;
      };

1016. queryString

  • 難度:medium
  • 題意解析:確認 1~N 的每一個數的二進制表示都是 S 的子串。
  • 思路:編寫一個數字轉二進制字符串方法(避開32位限制),for 數組轉換結果,肯定是否在 S 中都存在。(因爲只要一個二進制字符串不匹配就退出故實際運行時間應該是更低的)

    • 複雜度: 時間 O(n), 空間 O(N)
    • 實現:

      var queryString = function(S, N) {
          for (let i=0; i<=N; ++i) {
              if (S.indexOf(num2bin(i)) === -1) {
                  return false;
              }
          }
          return true;
      };
      
      function num2bin (num){
          let binStr = '';
          while(num>0) {
              binStr = num%2 + binStr;
              num = Math.floor(num/2);
          }
          return binStr;
      }

49. groupAnagrams

  • 難度:medium
  • 題意解析:將字符串數組中,字母組成相同的詞概括到同一數組內,再合併進數組。
  • 初始思路:遍歷數組,對item進行 sort 並做爲 map 的key,value 爲計數器的計數(也是結果數組的 index),根據map.has(key)的狀況,數組分別尾增新數組或在特定數組中尾插單詞。

    • 複雜度:時間 O(n), 空間複雜度O(n)
    • 實現:

      var groupAnagrams = function(strs) {
          let map = new Map();
          let resultArr = [];
          let count = 0;
          for (let i=0, arrLen=strs.length; i<arrLen; i++) {
              let tempStr = strs[i].split('').sort().join('');
              if (map.has(tempStr)) {
                  let index = map.get(tempStr);
                  resultArr[index].push(strs[i]);
              } else {
                  map.set(tempStr, count);
                  resultArr[count++] = [strs[i]];
              }
          }
          return resultArr;
      };
  • 優化思路:用 map 存放 a-z 映射到26個質數的鍵值對,用每次"拆分 item 對得到乘積" 替換 "sort item"的過程

    • 複雜度:同上. 中間的從 item 獲取 key 的過程被簡化了。
    • 實現:

      var groupAnagrams = function(strs) {
          var fixedObj={
              a:2,
              b:3,
              c:5,
              d:7,
              e:11,
              f:13,
              g:17,
              h:19,
              i:23,
              j:29,
              k:31,
              l:37,
              m:41,
              n:43,
              o:47,
              p:53,
              q:59,
              r:61,
              s:67,
              t:71,
              u:73,
              v:79,
              w:83,
              x:89,
              y:97,
              z:101
          }
          let map = new Map();
          let resultArr = [];
          let count = 0;
          for (let i=0, arrLen=strs.length; i<arrLen; i++) {
              // let tempStr = strs[i].split('').sort().join('');
              let uniqueNum = 1;
              for (let j=0, word=strs[i], len=word.length; j<len; j++) {
                  uniqueNum *= fixedObj[word[j]];
              }
              if (map.has(uniqueNum)) {
                  let index = map.get(uniqueNum);
                  resultArr[index].push(strs[i]);
              } else {
                  map.set(uniqueNum, count);
                  resultArr[count++] = [strs[i]];
              }
          }
          return resultArr;
      };

824. toGoatLatin

  • 難度:easy
  • 題意解析:句子可被空格分割爲 n 個單詞,每一個單詞處理以下:

    • 單詞開頭爲元音則尾部+ma+a*(單詞在數組中的下標+1);
    • 非元音開頭則單詞摘出開頭+開頭+ma+a*(單詞在數組中的下標+1);
  • 解題思路:按照題意編寫代碼

    • 實現:

      var toGoatLatin = function(S) {
          let arr = S.split(' ');
          for (let i=0, arrLen=arr.length; i<arrLen; i++) {
              if (/[aeiouAEIOU]/.test(arr[i][0])) {
                  arr[i] += 'ma'
              } else {
                  let tempArr = arr[i].split('');
                  tempArr = tempArr.splice(1, tempArr.length)
                  tempArr.push(arr[i][0])
                  tempArr.push('ma')
                  arr[i] = tempArr.join('') 
              }
              for (let j=0, len=i+1; j<len; j++) {
                  arr[i] += 'a'
              }
          }
          return arr.join(' ').trim()
      };

609.findDuplicate

  • 難度:medium
  • 題意解析:給定一個二維數組,每一個子數組第一個元素爲根目錄,第二到第 n 個元素爲文件+文件內容,目標是將相同文本內容的文件路徑名放入同一數組。
  • 初始思路:建立 map,雙 for 循環組裝出實際文件路徑,並將內容做爲key,數組爲 value 放入 map, 相同數組不斷插入 value,最後取 map.values() 整合出目標二維數組。

    • 實現:

      var findDuplicate = function(paths) {
          let map = new Map();
          for (let i=0, pathsLen=paths.length; i<pathsLen; i++) {
              let tempArr = paths[i].split(' ');
              let prefix = tempArr[0];
              for (let j=1, len=tempArr.length; j<len; j++) {
                  let fileArr = tempArr[j].split('(');
                  let pathName = prefix + '/' + fileArr[0];
                  let content = fileArr[1].split(')')[0];
                  if (!map.has(content)) {
                      map.set(content, [])
                  }
                  map.get(content).push(pathName)
              }
          }
          let resultArr = []
          for (let value of map.values()) {
              if (value.length > 1) {
                  resultArr.push(value)
              }
          }
          return resultArr
      };
  • 優化思路:

    • 優化點1:將中間的"兩次切分字符串"改成"字符串截取",減小了空間消耗;
    • 優化點2:最後從 map.values()生產目標二維數組的過程使用 ES6語法的 Array.from + filter 代替,提升執行效率(反作用是加大內存消耗);

      • 實現:

        var findDuplicate = function(paths) {
            let map = new Map();
            for (let i=0, pathsLen=paths.length; i<pathsLen; i++) {
                let tempArr = paths[i].split(' ');
                let prefix = tempArr[0];
                for (let j=1, len=tempArr.length; j<len; j++) {
                    let item = tempArr[j];
                    let contentIndex = item.indexOf('(');
                    let content = item.slice(contentIndex+1, item.length-1);
                    let pathName = prefix + '/' + item.slice(0, contentIndex);
                    if (!map.has(content)) {
                        map.set(content, [])
                    }
                    map.get(content).push(pathName)
                }
            }
            return Array.from(map.values()).filter(item=>item.length>1);
        };

49. groupAnagrams

  • 難度:medium
  • 題意解析:從包含數個字符串的數組中獲取包含字母徹底相同的字符串。
  • 初始解法:經過鍵值對方法,將每一個字符串的字母排序造成鍵,鍵相同的字符串放到一塊兒。

    • 複雜度:時間O(n)、空間O(n)
    • 實現:

      var groupAnagrams = function(strs) {
          let map = new Map();
          for (let i=0, strsLen=strs.length; i<strsLen; i++) {
              let sortStr = strs[i].split('').sort().join('')
              if (map.has(sortStr)) {
                  map.get(sortStr).push(strs[i])
              } else {
                  map.set(sortStr, [strs[i]])
              }
          }
          return Array.from(map.values());
      };

788. rotatedDigits

  • 難度:easy
  • 題意解析:計算 1-》N 中間的數字有多少個是好數,好數的定位爲180旋轉後仍爲數字且不與原數相等。即知足數字爲好數的前提是:

    • 1)翻轉後全部數字有效(0,1,2,5,6,8,9);
    • 2)至少一個數字爲不一樣數;(2,5,6,9)
  • 初始解法:全部數均爲有效=》沒有無效數字=》不包含(3,4,7), 故只要知足包含(2,5,6,9)且不包含(3,4,7)即符合要求,用正則能夠簡單得出結果。

    • 實現:

      var rotatedDigits = function(N) {
      let count = 0;
          for (let i=1, len=N+1; i<len; i++) {
              if (!(/[347]/g.test(i)) && /[2569]/g.test(i)) {
                  count++;
              }
          }
          return count;
      };

To Be Continue~

相關文章
相關標籤/搜索