返回介绍

solution / 0400-0499 / 0472.Concatenated Words / README

发布于 2024-06-17 01:04:00 字数 6550 浏览 0 评论 0 收藏 0

472. 连接词

English Version

题目描述

给你一个 不含重复 单词的字符串数组 words ,请你找出并返回 words 中的所有 连接词

连接词 定义为:一个完全由给定数组中的至少两个较短单词(不一定是不同的两个单词)组成的字符串。

 

示例 1:

输入:words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
输出:["catsdogcats","dogcatsdog","ratcatdogcat"]
解释:"catsdogcats" 由 "cats", "dog" 和 "cats" 组成; 
   "dogcatsdog" 由 "dog", "cats" 和 "dog" 组成; 
   "ratcatdogcat" 由 "rat", "cat", "dog" 和 "cat" 组成。

示例 2:

输入:words = ["cat","dog","catdog"]
输出:["catdog"]

 

提示:

  • 1 <= words.length <= 104
  • 1 <= words[i].length <= 30
  • words[i] 仅由小写英文字母组成。 
  • words 中的所有字符串都是 唯一 的。
  • 1 <= sum(words[i].length) <= 105

解法

方法一:前缀树 + DFS

判断一个单词是不是连接词,需要判断这个单词是否完全由至少两个给定数组中的更短的非空单词(可以重复)组成。判断更短的单词是否在给定数组中,可以使用字典树实现。

首先将 $words$ 按照字符串的长度递增的顺序排序,排序后可以确保当遍历到任意单词时,比该单词短的单词一定都已经遍历过,因此可以根据已经遍历过的全部单词判断当前单词是不是连接词。

在将 $words$ 排序之后,遍历 $words$,跳过空字符串,对于每个非空单词,判断该单词是不是连接词,如果是连接词则将该单词加入结果数组,如果不是连接词则将该单词加入字典树。

判断一个单词是不是连接词的做法是在字典树中深度优先搜索。从该单词的第一个字符(即下标 $0$ 处的字符)开始,在字典树中依次搜索每个字符对应的结点,可能有以下几种情况:

  • 如果一个字符对应的结点是单词的结尾,则找到了一个更短的单词,从该字符的后一个字符开始搜索下一个更短的单词;
  • 如果一个字符对应的结点在字典树中不存在,则当前的搜索结果失败,回到上一个单词的结尾继续搜索。

如果找到一个更短的单词且这个更短的单词的最后一个字符是当前单词的最后一个字符,则当前单词是连接词。由于数组 $words$ 中没有重复的单词,因此在判断一个单词是不是连接词时,该单词一定没有加入字典树,由此可以确保判断连接词的条件成立。

说明:由于一个连接词由多个更短的非空单词组成,如果存在一个较长的连接词的组成部分之一是一个较短的连接词,则一定可以将这个较短的连接词换成多个更短的非空单词,因此不需要将连接词加入字典树

class Trie:
  def __init__(self):
    self.children = [None] * 26
    self.is_end = False

  def insert(self, w):
    node = self
    for c in w:
      idx = ord(c) - ord('a')
      if node.children[idx] is None:
        node.children[idx] = Trie()
      node = node.children[idx]
    node.is_end = True


class Solution:
  def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]:
    def dfs(w):
      if not w:
        return True
      node = trie
      for i, c in enumerate(w):
        idx = ord(c) - ord('a')
        if node.children[idx] is None:
          return False
        node = node.children[idx]
        if node.is_end and dfs(w[i + 1 :]):
          return True
      return False

    trie = Trie()
    ans = []
    words.sort(key=lambda x: len(x))
    for w in words:
      if dfs(w):
        ans.append(w)
      else:
        trie.insert(w)
    return ans
class Trie {
  Trie[] children = new Trie[26];
  boolean isEnd;

  void insert(String w) {
    Trie node = this;
    for (char c : w.toCharArray()) {
      c -= 'a';
      if (node.children[c] == null) {
        node.children[c] = new Trie();
      }
      node = node.children[c];
    }
    node.isEnd = true;
  }
}

class Solution {
  private Trie trie = new Trie();

  public List<String> findAllConcatenatedWordsInADict(String[] words) {
    Arrays.sort(words, (a, b) -> a.length() - b.length());
    List<String> ans = new ArrayList<>();
    for (String w : words) {
      if (dfs(w)) {
        ans.add(w);
      } else {
        trie.insert(w);
      }
    }
    return ans;
  }

  private boolean dfs(String w) {
    if ("".equals(w)) {
      return true;
    }
    Trie node = trie;
    for (int i = 0; i < w.length(); ++i) {
      int idx = w.charAt(i) - 'a';
      if (node.children[idx] == null) {
        return false;
      }
      node = node.children[idx];
      if (node.isEnd && dfs(w.substring(i + 1))) {
        return true;
      }
    }
    return false;
  }
}
class Trie {
public:
  vector<Trie*> children;
  bool isEnd;
  Trie()
    : children(26)
    , isEnd(false) {}

  void insert(string w) {
    Trie* node = this;
    for (char c : w) {
      c -= 'a';
      if (!node->children[c]) node->children[c] = new Trie();
      node = node->children[c];
    }
    node->isEnd = true;
  }
};

class Solution {
public:
  Trie* trie = new Trie();

  vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
    sort(words.begin(), words.end(), [&](const string& a, const string& b) {
      return a.size() < b.size();
    });
    vector<string> ans;
    for (auto& w : words) {
      if (dfs(w))
        ans.push_back(w);
      else
        trie->insert(w);
    }
    return ans;
  }

  bool dfs(string w) {
    if (w == "") return true;
    Trie* node = trie;
    for (int i = 0; i < w.size(); ++i) {
      int idx = w[i] - 'a';
      if (!node->children[idx]) return false;
      node = node->children[idx];
      if (node->isEnd && dfs(w.substr(i + 1))) return true;
    }
    return false;
  }
};
type Trie struct {
  children [26]*Trie
  isEnd  bool
}

func newTrie() *Trie {
  return &Trie{}
}
func (this *Trie) insert(word string) {
  node := this
  for _, c := range word {
    c -= 'a'
    if node.children[c] == nil {
      node.children[c] = newTrie()
    }
    node = node.children[c]
  }
  node.isEnd = true
}

func findAllConcatenatedWordsInADict(words []string) (ans []string) {
  sort.Slice(words, func(i, j int) bool { return len(words[i]) < len(words[j]) })
  trie := newTrie()
  var dfs func(string) bool
  dfs = func(w string) bool {
    if w == "" {
      return true
    }
    node := trie
    for i, c := range w {
      c -= 'a'
      if node.children[c] == nil {
        return false
      }
      node = node.children[c]
      if node.isEnd && dfs(w[i+1:]) {
        return true
      }
    }
    return false
  }
  for _, w := range words {
    if dfs(w) {
      ans = append(ans, w)
    } else {
      trie.insert(w)
    }
  }
  return
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文