统计追加字母可以获得的单词数

814 阅读3分钟

5978. 统计追加字母可以获得的单词数

题目

给你两个下标从 0 开始的字符串数组 startWords 和 targetWords 。每个字符串都仅由 小写英文字母 组成。

对于 targetWords 中的每个字符串,检查是否能够从 startWords 中选出一个字符串,执行一次 转换操作 ,得到的结果与当前 targetWords 字符串相等。

转换操作 如下面两步所述:

追加 任何 不存在 于当前字符串的任一小写字母到当前字符串的末尾。 例如,如果字符串为 "abc" ,那么字母 'd'、'e' 或 'y' 都可以加到该字符串末尾,但 'a' 就不行。如果追加的是 'd' ,那么结果字符串为 "abcd" 。 重排 新字符串中的字母,可以按 任意 顺序重新排布字母。 例如,"abcd" 可以重排为 "acbd"、"bacd"、"cbda",以此类推。注意,它也可以重排为 "abcd" 自身。 找出 targetWords 中有多少字符串能够由 startWords 中的 任一 字符串执行上述转换操作获得。返回 targetWords 中这类 字符串的数目 。

注意:你仅能验证 targetWords 中的字符串是否可以由 startWords 中的某个字符串经执行操作获得。startWords  中的字符串在这一过程中 不 发生实际变更。

示例1

输入:startWords = ["ant","act","tack"], targetWords = ["tack","act","acti"]
输出:2
解释:
- 为了形成 targetWords[0] = "tack" ,可以选用 startWords[1] = "act" ,追加字母 'k' ,并重排 "actk""tack" 。
- startWords 中不存在可以用于获得 targetWords[1] = "act" 的字符串。
  注意 "act" 确实存在于 startWords ,但是 必须 在重排前给这个字符串追加一个字母。
- 为了形成 targetWords[2] = "acti" ,可以选用 startWords[1] = "act" ,追加字母 'i' ,并重排 "acti""acti" 自身。

前言

实话讲,读题花了10分钟;大神们4题做完才花了14分钟;语文不好,影响学习。囧

题解

暴力求解

  • 所有的新字符串只能从startWords生成
  • 那就先根据startWords数组,枚举所有生成新字符串的可能;
  • 比如startWords=['a'],必然可以生成['ab','ac','ad',...,'ay','az']
  • 将根据比如startWords生成的字符串根据ascii码排序放入哈希表map中
  • 枚举targetWords中字符串,如果targetWords中字符串可以再map中找到,数量+1
  • 返回结果

暴力求解代码

var wordCount = function (startWords, targetWords) {
  let map = {}
  startWords.forEach((k) => {
    const list = k.split('')
    for (let i = 0; i < 26; i++) {
      const s = String.fromCharCode(97 + i)
      if (!list.includes(s)) {
        const t = list.concat([s])
        const k = t.sort().join('')
        map[k] = 1
      }
    }
  })
  let result = 0
  targetWords.forEach((v) => {
    const s = v.split('').sort().join('')
    if (map[s] > 0) result++
  })
  return result
}

状态压缩

上一个思路是从startWords生成全部的可能的字符串,这个思路是从targetWords,枚举targetWords中字符串v,减少字符串任意1个字符,查看startWords是否存在,如果存在,该字符串v可以被转换

如何才能快速的实现上述操作呢;

假设startWords = ['ac', 'a'],targetWords = ['acd', 'ab', 'a'] ;
遍历startWords将字符串放入哈希表map中,map = {'ac','a'}

枚举targetWords,得到字符串'acd', 'ab', 'a';

对于'acd',可以由'cd'+'a'、'ad'+'c'、'ac'+'d';得到;在'ac'+'d'这个解中'ac'明显可以从map中查到,所以acd可以被startWords的字符串转换;

根据上述思路转编辑代码如下

代码

var wordCount = function (startWords, targetWords) {
  const set = new Set()
  const getIndex = (s) => s.charCodeAt() - 'a'.charCodeAt()

  startWords.forEach((word) => {
    let num = 0
    for (let i = 0; i < word.length; i++) {
      num |= 1 << getIndex(word[i])
    }
    set.add(num)
  })
  console.log('set', set)
  // a b d
  // a b c d

  let result = 0

  for (const target of targetWords) {
    let x = 0
    for (let i = 0; i < target.length; i++) {
      x |= 1 << getIndex(target[i])
    }

    for (let i = 0; i < target.length; i++) {
      let t = 1 << getIndex(target[i])
     
      const k = x ^ t
      if (set.has(k)) {
        result++
        break
      }
    }
  }

  return result
}