高级前端手写代码【Q607】关于字符串编码解码进阶

关于字符串编码解码进阶

更多描述 一道有意思的面试题 例子如下,实现countOfLetters

countOfLetters("A2B3"); // { A: 2, B: 3 }
countOfLetters("A(A3B)2"); // { A: 7, B: 2}
countOfLetters("C4(A(A3B)2)2"); // { A: 14, B: 4, C: 4 }

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 625

Author 回答者: Nctdt

答案:

type LetterCounter = {
  // A-Z
  [i: string]: number;
};
 
function letterAddCount(target: LetterCounter, source: LetterCounter) {
  for (let k in source) {
    target[k] ??= 0;
    target[k] += source[k];
  }
  return target;
}
function letterMultipleCount(target: LetterCounter, multiples: number) {
  for (let i in target) {
    target[i] *= multiples;
  }
  return target;
}
function countOfLetters(str: string) {
  const regex = /[1-9]/;
  const stack: LetterCounter[] = [{}];
  for (let i = 0; i < str.length; i++) {
    const ch = str[i];
    let count = 1;
    if (regex.test(str[i + 1])) count = +str[++i]; // case ( | )
    switch (ch) {
      case "(":
        stack.push({});
        continue;
      case ")":
        const pop = stack.pop()!;
        const last = stack[stack.length - 1];
        letterAddCount(last, letterMultipleCount(pop, count));
        continue;
    } // case A-Z
    const last = stack[stack.length - 1];
    last[ch] ??= 0;
    last[ch] += count;
  }
  return stack.pop();
}

Author 回答者: haotie1990

function countOfLetters(str) {
  let stack = [];
  const len = str.length;
  const pick = () => stack[stack.length - 1];
  const isNumber = (v) => !Number.isNaN(parseInt(v));
  for (let i = 0; i < len; i++) {
    const s = str[i];
    if (pick() === ")" && isNumber(s)) {
      let subStr = "";
      while (pick() !== "(") {
        let letter = stack.pop();
        if (letter !== ")") {
          if (isNumber(letter)) {
            subStr = +letter * parseInt(s) + subStr;
          } else if (isNumber(subStr.charAt(0))) {
            // 字母后跟着数字则直接拼接
            subStr = letter + subStr;
          } else {
            subStr = letter + s + subStr; // 字母后没有跟数字,需要将外层数字累加
          }
        }
      }
      // 弹出'('
      stack.pop();
      // 重新入栈
      stack = stack.concat(subStr.split(""));
      continue;
    }
    stack.push(s);
  }
 
  let result = {};
  let count = "";
  while (stack.length) {
    const s = stack.pop();
    if (isNumber(s)) {
      count = s + count;
    } else {
      result[s] = (result[s] || 0) + parseInt(count || "1");
      count = "";
    }
  }
  return result;
}

Author 回答者: hwb2017

不用栈,全部用正则实现

const countOfLetters = (str) => {
  let frequencyMap = {};
  let regArray = [
    /([a-zA-Z])([a-zA-Z])/g, //AA
    /([a-zA-Z])(\))/g, //A)
    /([a-zA-Z])(\()/g, //A(
    /(\))([a-zA-Z])/g, //)A
    /(\))(\))/g, //))
    /(\))(\()/g, //)(
  ];
  let targetStr = str;
  for (const reg of regArray) {
    targetStr = targetStr.replace(reg, "$11$2");
  }
  // let targetStr = str.replace(/([a-zA-Z]|\))(\(|[a-zA-Z]|\))/g,"$11$2") // 这种写法最后一个测试用例会通过不了
  let unfoldable = /(\([0-9a-zA-Z]*\))([0-9]+)/;
  while (unfoldable.test(targetStr)) {
    targetStr = targetStr.replace(unfoldable, (match, p1, p2) =>
      p1.slice(1, -1).replace(/[0-9]+/g, (count) => +count * p2),
    );
  }
  let matchResult;
  let unit = /[a-zA-Z][0-9]+/g;
  while ((matchResult = unit.exec(targetStr)) !== null) {
    let letter = matchResult[0][0];
    let frequency = matchResult[0].slice(1);
    frequencyMap[letter] = frequencyMap[letter]
      ? frequencyMap[letter] + Number(frequency)
      : +frequency;
  }
  return frequencyMap;
};
 
console.log(countOfLetters("A2B3"));
console.log(countOfLetters("A(A3B)2"));
console.log(countOfLetters("C4(A(A3B)2)2"));
console.log(countOfLetters("C4(A()2)2"));
console.log(countOfLetters("(A2B3)"));
console.log(countOfLetters("(A11B9)11"));
console.log(countOfLetters("(A2B3)(A5B6)"));
console.log(countOfLetters("(A2B3)C(A5B6)"));

Author 回答者: JamiLanister

function replaceSubstring(str, start, end, replacement) {
    const startstr = str.substring(0, start)
    const endstr = str.substring(end+1)
    return startstr + replacement + endstr
}

function countOfLetters(s) {
    const flatObj = {}
    function handle(slices) {
        let left = 0;
        let right = slices.length-1
        let str = slices
        while (left < right ) {
            if (slices[left] === '(') {
                while (left < right) {
                    if (slices[right] === ')') {
                        const res = handle(slices.slice(left+1, right))
                        let num = Number(slices[right+1])
                        if (!isNaN(num)) {
                            str = replaceSubstring(slices, left, right+1, res.repeat(num))
                        }
                        return str
                    } else {
                        right--
                    }
                }
            } else {
                left++;
            }
        }

        return str
    }
    const flated = handle(s)
    for (let index=0; index<flated.length; index++) {
        const char = flated[index]
        const nextChar = flated[index+1]
        if (char.match(/\d/)) {
            continue
        }
        console.log(char, nextChar)
        if (!isNaN(Number(nextChar))) {
            flatObj[char] = flatObj[char] ? flatObj[char] + Number(nextChar) : Number(nextChar)
        } else {
            flatObj[char] = flatObj[char] ? flatObj[char] + 1 : 1
        }
    }
    return flatObj
}

console.log(countOfLetters("C4(A(A3B)2)2")) // { A: 14, B: 4, C: 4 }