【第2期】前端算法精選-字符串系列

今天精選的題目是關於字符串操做的,涉及到的技巧有字符串的滑動窗口思路、大數相乘。前端

字符串的排列

LeetCode.567,難度中等微信

題目

給定兩個字符串s1s2,寫一個函數來判斷s2是否包含s1的排列。函數

換句話說,第一個字符串的排列之一是第二個字符串的子串。spa

示例1:code

輸入: s1 = "ab" s2 = "eidbaooo"
輸出: True
解釋: s2 包含 s1 的排列之一 ("ba").ip

示例2:rem

輸入: s1= "ab" s2 = "eidboaoo"
輸出: False字符串

注意:string

  1. 輸入的字符串只包含小寫字母
  2. 兩個字符串的長度都在 [1, 10,000] 之間

解法

理解一下題意,要求s1的字符串排列之一是第二個字符串的子串,好比ab是eidbaooo的子串,ba也是eidbaooo的子串,一樣的baoo也是eidbaooo的子串,因此這個規則能夠抽象一下,若是eidbaooo裏面存在一個子串,該子串的字符和s1的字符相同,且該子串字符出現的次數和s1中的字符出現次數相同,所以,能夠考慮滑動窗口的思路。it

首先維護一個map1來保存s1的字符以及每一個字符出現的次數,再維護一個map2來保存當前窗口中的每一個字符以及每一個字符出現的次數,若是map1和map2相同,則說明滑動窗口中的字符子串和s1的排列之一相同,不然繼續往右滑動。

  1. 初始化,以s1長度爲準,來構造兩個map,map1 = { a: 1, b: 1 },map2 = { e: 1, i: 1 }。此時滑動窗口中的字符串是'ei'
  2. 從s2的第3個(前兩個已經被初始化過)字符'd'開始遍歷,map1和map2不一樣。此時窗口字符串是'ei',刪掉左邊的'e',更新map2= { i: 1 };加入第3個字符'd',更新map2 = { i: 1, d: 1 }
  3. 此時遍歷到第4個字符'b',map1和map2不一樣。此時窗口字符串是'id',刪掉左邊的'i',更新map2={ d: 1 };加入第4個字符串'b',更新map2={ d: 1, b: 1 }
  4. 此時遍歷到第5個字符'a',map1和map2不一樣。此時窗口的字符串是'db',刪掉左邊的'd',更新map2={ b: 1 };加入第5個字符串'a',更新map2={ b: 1, a: 1 }
  5. 此時遍歷到滴6個字符'o',map1和map2相同,返回true

代碼以下:

/\*\*
 \* @param {string} s1
 \* @param {string} s2
 \* @return {boolean}
 \*/
let checkMapEqual = function(m1, m2) { 
    let keys1 = Object.keys(m1), keys2 = Object.keys(m2);
    
    if(keys1.length !== keys2.length)
        return false;
    
    for(let i = 0;i < keys1.length;i++) {
        const curKey = keys1\[i\];
        if(m1\[curKey\] !== m2\[curKey\])
            return false;
    }
    
    return true;
}
let checkInclusion = function(s1, s2) {
    if(s1.length > s2.length)
      return false;
    
    let map1 = {}, map2 = {};
    const len1 = s1.length, len2 = s2.length;
    // 初始化map
    for(let i = 0;i < len1;i++) {
        if(map1\[s1\[i\]\]) {
            map1\[s1\[i\]\]++
        } else {
            map1\[s1\[i\]\] = 1;
        }
        if(map2\[s2\[i\]\]) {
            map2\[s2\[i\]\]++
        } else {
            map2\[s2\[i\]\] = 1;
        }
    }
    for(let i = len1;i < len2;i++) {
        if(checkMapEqual(map1, map2)) {
            return true;
        }
        
        // 將窗口最左邊的字符刪去
        const leftChar = s2\[i-len1\];
        if(map2\[leftChar\] === 1) {
            delete map2\[leftChar\];
        } else {
            map2\[leftChar\]--;
        }
        // 在窗口右邊加入一個字符
        const rightChar = s2\[i\];
        if(map2\[rightChar\]) {
            map2\[rightChar\]++;
        } else {
            map2\[rightChar\] = 1;
        }
    }
    return checkMapEqual(map1, map2);
};

字符串相乘

LeetCode.43,難度中等

題目

給定兩個以字符串形式表示的非負整數num1num2,返回num1num2的乘積,它們的乘積也表示爲字符串形式。

示例 1:

輸入: num1 = "2", num2 = "3"
輸出: "6"

示例 2:

輸入: num1 = "123", num2 = "456"
輸出: "56088"

說明:

  1. num1num2的長度小於110。
  2. num1num2只包含數字0-9
  3. num1num2均不以零開頭,除非是數字 0 自己。
  4. 不能使用任何標準庫的大數類型(好比 BigInteger)或直接將輸入轉換爲整數來處理。

解法

先分析一下題目字符串相乘,要想模擬兩個數字相乘,須要解決逐位相乘、再逐個相加的問題,過程當中須要注意正確進位,好比'123'和'456'相乘:

  1. 3和456相乘,獲得1368;
  2. 2和456相乘,獲得912,由於2是十位,因此獲得9120;
  3. 1和456相乘,獲得456,由於1是百位,因此獲得45600;
  4. 把3次乘積相加,加的過程當中也要注意逐位相加,別忘了進位

代碼以下:

/\*\*
 \* @param {string} num1
 \* @param {string} num2
 \* @return {string}
 \*/
const multiplyStep = (num1, pos, num2) => {
  let res = '';
  let carry = 0;
  for(let i = num2.length-1;i >= 0;i--) {
    const cur = num2\[i\];
    let product = parseInt(cur)\*num1;
    if(carry) {
      product += carry;
      carry = 0;
    }
    if(product >= 10) {
      carry = parseInt(product/10);
      product = product%10
    }
    res = product + res;
  }
  if(carry) {
    res = carry + res;
  }
  
  while(pos) {
    res += 0;
    pos--;
  }
  return res;
}
const addStep = (num1, num2) => {
  let len1 = num1.length;
  let len2 = num2.length;
  let num1Reversed = num1.split('').reverse().join('');
  let num2Reversed = num2.split('').reverse().join('');
  
  let index = 0;
  let res = '';
  let carry = false;
  while(index < len1 && index < len2) {
    const char1 = num1Reversed\[index\];
    const char2 = num2Reversed\[index\];
    
    let sum = parseInt(char1) + parseInt(char2);
    if(carry) {
      sum += 1;
      carry = false;
    }
    if(sum >= 10) {
      carry = true;
      sum = sum%10;
    }
    res += sum;
    index++;
  }
  while(index < len1) {
    let cur = num1Reversed\[index\];
    if(carry) {
      carry = false
      cur =  parseInt(cur)+1;
    }
    if(cur >= 10) {
      cur = cur%10;
      carry = true;
    }
    res += cur;
    index++;
  }
  while(index < len2) {
    let cur = num2Reversed\[index\];
    if(carry) {
      carry = false
      cur =  parseInt(cur)+1;
    }
    if(cur >= 10) {
      cur = cur%10;
      carry = true;
    }
    res += cur;
    index++;
  }
  if(carry) {
    res += 1;
    carry = false;
  }
  
  return res.split('').reverse().join('');
}
var multiply = function(num1, num2) {
  if(num1 === '0' || num2 === '0')
    return '0';
  
  let strArr = \[\];
  let res = '0';
  for(let i = num1.length-1;i >= 0;i--) {
    strArr.push(
      multiplyStep(num1\[i\], num1.length-1-i, num2)
    )
  }
    console.log(strArr);
  for(let i = 0;i < strArr.length;i++)  {
    res = addStep(res, strArr\[i\]);
  }
  
  return res;
};

感興趣的能夠關注個人微信公衆號,前端亞古獸

相關文章
相關標籤/搜索