Skip to content
RSS feed tkhwang on GitHub tkhwang on Twitter

πŸ€– leetcode 1239. Maximum Length of a Concatenated String with Unique Characters | medium | bitset | javascript

πŸ—’οΈ Problems

Maximum Length of a Concatenated String with Unique Characters - LeetCode

You are given an array of strings arr.
A string s is formed by the concatenation of a subsequence of arr
that has unique characters.

Return the maximum possible length of s.

A subsequence is an array that can be derived from another array
by deleting some or no elements without changing the order of the remaining elements.
Input: arr = ["un","iq","ue"]
Output: 4
Explanation: All the valid concatenations are:
- ""
- "un"
- "iq"
- "ue"
- "uniq" ("un" + "iq")
- "ique" ("iq" + "ue")
Maximum length is 4.

πŸ€” First attempt

  • i번째 word λ₯Ό μ„ νƒν•œ 경우, μ„ νƒν•˜μ§€ μ•Šμ€ κ²½μš°μ— λŒ€ν•΄μ„œ 처리.
  • ν˜„μž¬κΉŒμ§€ μΆ”κ°€λœ word λ₯Ό character frequency map 을 가지고 λ‹€λ‹ˆλ©΄μ„œ conflict κ°€ μžˆλŠ”μ§€ λΉ„κ΅ν•˜κΈ°.
var maxLength = function (arr) {
  const N = arr.length;
  let max = -Infinity;

  const dfs = (index, cur, set) => {
    if (index >= N) {
      if (max < cur.length) max = cur.length;
      return;
    }

    // not add
    dfs(index + 1, cur, set);

    // add
    const localSet = new Set();
    for (const ch of arr[index]) {
      if (localSet.has(ch)) return;
      localSet.add(ch);
    }
    for (const ch of arr[index]) {
      if (set.has(ch)) return;
    }

    for (const ch of arr[index]) {
      set.add(ch);
    }
    dfs(index + 1, cur + arr[index], set);

    for (const ch of arr[index]) {
      set.delete(ch);
    }
  };

  dfs(0, "", new Set());

  return max;
};

character 의 쀑볡 μ—¬λΆ€ 체크λ₯Ό character frequencyλ₯Ό object 둜 μ²˜λ¦¬ν–ˆλŠ”λ°... <br /> 이 뢀뢄이 κΉ”λ”ν•˜μ§€ μ•Šμ•„μ„œ bitset μ΄μš©ν•œ 방법이 더 쒋은 것 κ°™μ•„μ„œ μ•„λž˜ μ •λ¦¬ν•΄λ΄…λ‹ˆλ‹€.

✨ Idea

  • 주어진 array index [0, ..., N-1] recursive 둜 μˆœνšŒν•˜λ©΄μ„œ
  • ν•΄λ‹Ή word λ₯Ό μΆ”κ°€ν•˜κ±°λ‚˜ μΆ”κ°€ν•˜μ§€ μ•Šκ±°λ‚˜ 선택.
  • μΆ”κ°€ν•˜λŠ” κ²½μš°μ—λŠ” κΈ°μ‘΄ μΆ”κ°€λœ characterλ“€κ³Ό conflict κ°€ μ—†λŠ”μ§€λ₯Ό ν™•μΈν•΄μ„œ 처리.
  • 이 character 의 conflict 처리λ₯Ό μœ„ν•˜μ—¬ bitset μ΄μš©ν•¨.

πŸ€ Intuition

bitset

alphabet character의 쀑뢀 μ—¬λΆ€ 체크λ₯Ό μœ„ν•˜μ—¬ alphabet 의 μˆœμ„œλŒ€λ‘œ ν•΄λ‹Ή bit 에 μ €μž₯ν•˜κΈ°.

// "ab" 의 경우

a  b  c  ...  z
0  1  2  ...  26

1  1  0  ...   0

πŸ’‘ bitset κ°’ λˆ„μ  (bitwise OR)

  • ν•΄λ‹Ή word μ—μ„œ ch character λ‹¨μœ„λ‘œ μ²˜λ¦¬ν•˜λ©΄μ„œ
  • ν•΄λ‹Ή ch μœ„μΉ˜λ₯Ό 계산 (ch.charCodeAt(0) - 'a'.charCodeAt(0)) ν•΄μ„œ
  • μ•žμ˜ ch와 conflict κ°€ μ—†μœΌλ©΄ bitwise OR ν•΄μ„œ 값을 λˆ„μ ν•˜κ³  μ΅œμ’… return 함.
const getSet = (word) => {
  let b = 0;
  for (const ch of word) {
    const d = ch.charCodeAt(0) - "a".charCodeAt(0);
    if ((b & (1 << d)) > 0) return -1;

    b |= 1 << d;
  }
  return b;
};

πŸ’‘ bitset 확인 (bitwise AND)

  • character 의 conflict λΉ„κ΅λŠ” character λ³„λ‘œ ν•˜λ‚˜μ”© λΉ„κ΅ν•˜λŠ” 것이 μ•„λ‹ˆλΌ bitset으둜 λ³€κ²½λœ 값을 bitwise and ν•΄μ„œ 확인할 수 있음.
    • === 0 : conflict μ—†λŠ” 경우
    • > 0 : ν•΄λ‹Ή bit λŠ” 1 이 λ˜λ―€λ‘œ 0 μ΄μƒμ˜ 값을 κ°–κ²Œ 됨.
  • conflict κ°€ μ—†λŠ” 쑰건 확인
    • ν•΄λ‹Ή word (arr[index])의 lookup[index] κ°€ μ‘΄μž¬ν•΄μ•Όν•˜λ©° (word 내에 conflict κ°€ μžˆλ‹€λ©΄ lookup μžμ²΄κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠμŒ.)
    • κΈ°μ‘΄ bitset κ³Ό bitwise AND ν•œ κ²°κ³Όκ°€ 0 으둜 conflict κ°€ μ—†μ–΄μ•Ό ν•œλ‹€.
    const dfs = (index, currentS, currentL) => {
        ...
        // use
        if (lookup[index] !== -1 && (currentS & lookup[index]) === 0) {
            dfs(index + 1, currentS | lookup[index], currentL + arr[index].length)
        }
    }

πŸ”₯ My Solution

/**
 * @param {string[]} arr
 * @return {number}
 */
var maxLength = function (arr) {
  const N = arr.length;

  const lookup = [];

  const getSet = (word) => {
    let b = 0;
    for (const ch of word) {
      const d = ch.charCodeAt(0) - "a".charCodeAt(0);
      if ((b & (1 << d)) > 0) return -1;

      b |= 1 << d;
    }
    return b;
  };

  let max = -Infinity;

  const dfs = (index, currentS, currentL) => {
    if (index >= N) {
      if (max < currentL) max = currentL;
      return;
    }

    // not use
    dfs(index + 1, currentS, currentL);

    // use
    if (lookup[index] !== -1 && (currentS & lookup[index]) === 0) {
      dfs(index + 1, currentS | lookup[index], currentL + arr[index].length);
    }
  };

  for (const word of arr) {
    lookup.push(getSet(word));
  }

  dfs(0, 0, 0);

  return max;
};