diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/README.md b/solution/0700-0799/0720.Longest Word in Dictionary/README.md index 8f441c16a01eb..2b5851f0e2a22 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/README.md +++ b/solution/0700-0799/0720.Longest Word in Dictionary/README.md @@ -41,7 +41,7 @@ tags:
 输入:words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
 输出:"apple"
-解释:"apply" 和 "apple" 都能由词典中的单词组成。但是 "apple" 的字典序小于 "apply" 
+解释:"apply" 和 "apple" 都能由词典中的单词组成。但是 "apple" 的字典序小于 "apply"
 

 

@@ -60,121 +60,202 @@ tags: -### 方法一:哈希表 +### 方法一:字典树 -用哈希表存放所有单词。遍历这些单词,找出**长度最长且字典序最小**的单词。 +我们可以使用字典树来存储所有的单词,然后遍历所有的单词,判断当前单词是否可以由字典树中的其他单词逐步添加一个字母组成,找出满足条件的最长的,且字典序最小的单词。 + +时间复杂度 $O(L)$,空间复杂度 $O(L)$,其中 $L$ 是所有单词的长度之和。 #### Python3 ```python +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + self.is_end = False + + def insert(self, w: str): + 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 + + def search(self, w: str) -> bool: + node = self + for c in w: + idx = ord(c) - ord("a") + if node.children[idx] is None: + return False + node = node.children[idx] + if not node.is_end: + return False + return True + + class Solution: def longestWord(self, words: List[str]) -> str: - cnt, ans = 0, '' - s = set(words) - for w in s: - n = len(w) - if all(w[:i] in s for i in range(1, n)): - if cnt < n: - cnt, ans = n, w - elif cnt == n and w < ans: - ans = w + trie = Trie() + for w in words: + trie.insert(w) + ans = "" + for w in words: + if trie.search(w) and ( + len(ans) < len(w) or (len(ans) == len(w) and ans > w) + ): + ans = w return ans ``` #### Java ```java -class Solution { - private Set s; - - public String longestWord(String[] words) { - s = new HashSet<>(Arrays.asList(words)); - int cnt = 0; - String ans = ""; - for (String w : s) { - int n = w.length(); - if (check(w)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w.compareTo(ans) < 0) { - ans = w; - } +class Trie { + private Trie[] children = new Trie[26]; + private boolean isEnd = false; + + public void insert(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; } - return ans; + node.isEnd = true; } - private boolean check(String word) { - for (int i = 1, n = word.length(); i < n; ++i) { - if (!s.contains(word.substring(0, i))) { + public boolean search(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null || !node.children[idx].isEnd) { return false; } + node = node.children[idx]; } return true; } } + +class Solution { + public String longestWord(String[] words) { + Trie trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + String ans = ""; + for (String w : words) { + if (trie.search(w) + && (ans.length() < w.length() + || (ans.length() == w.length() && w.compareTo(ans) < 0))) { + ans = w; + } + } + return ans; + } +} ``` #### C++ ```cpp -class Solution { +class Trie { public: - string longestWord(vector& words) { - unordered_set s(words.begin(), words.end()); - int cnt = 0; - string ans = ""; - for (auto w : s) { - int n = w.size(); - if (check(w, s)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w < ans) - ans = w; + Trie* children[26] = {nullptr}; + bool isEnd = false; + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); } + node = node->children[idx]; } - return ans; + node->isEnd = true; } - bool check(string& word, unordered_set& s) { - for (int i = 1, n = word.size(); i < n; ++i) - if (!s.count(word.substr(0, i))) + bool search(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr || !node->children[idx]->isEnd) { return false; + } + node = node->children[idx]; + } return true; } }; + +class Solution { +public: + string longestWord(vector& words) { + Trie trie; + for (const string& w : words) { + trie.insert(w); + } + + string ans = ""; + for (const string& w : words) { + if (trie.search(w) && (ans.length() < w.length() || (ans.length() == w.length() && w < ans))) { + ans = w; + } + } + return ans; + } +}; ``` #### Go ```go +type Trie struct { + children [26]*Trie + isEnd bool +} + +func (t *Trie) insert(w string) { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } + node.isEnd = true +} + +func (t *Trie) search(w string) bool { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil || !node.children[idx].isEnd { + return false + } + node = node.children[idx] + } + return true +} + func longestWord(words []string) string { - s := make(map[string]bool) + trie := &Trie{} for _, w := range words { - s[w] = true + trie.insert(w) } - cnt := 0 + ans := "" - check := func(word string) bool { - for i, n := 1, len(word); i < n; i++ { - if !s[word[:i]] { - return false - } - } - return true - } - for w, _ := range s { - n := len(w) - if check(w) { - if cnt < n { - cnt, ans = n, w - } else if cnt == n && w < ans { - ans = w - } + for _, w := range words { + if trie.search(w) && (len(ans) < len(w) || (len(ans) == len(w) && w < ans)) { + ans = w } } return ans @@ -184,63 +265,165 @@ func longestWord(words []string) string { #### TypeScript ```ts -function longestWord(words: string[]): string { - words.sort((a, b) => { - const n = a.length; - const m = b.length; - if (n === m) { - return a < b ? -1 : 1; - } - return m - n; - }); - for (const word of words) { - let isPass = true; - for (let i = 1; i <= word.length; i++) { - if (!words.includes(word.slice(0, i))) { - isPass = false; - break; +class Trie { + children: (Trie | null)[] = new Array(26).fill(null); + isEnd: boolean = false; + + insert(w: string): void { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); } + node = node.children[idx]!; } - if (isPass) { - return word; + node.isEnd = true; + } + + search(w: string): boolean { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx]!.isEnd) { + return false; + } + node = node.children[idx]!; } + return true; } - return ''; +} + +function longestWord(words: string[]): string { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; + } + } + return ans; } ``` #### Rust ```rust -impl Solution { - pub fn longest_word(mut words: Vec) -> String { - words.sort_unstable_by(|a, b| (b.len(), a).cmp(&(a.len(), b))); - for word in words.iter() { - let mut is_pass = true; - for i in 1..=word.len() { - if !words.contains(&word[..i].to_string()) { - is_pass = false; - break; - } +struct Trie { + children: [Option>; 26], + is_end: bool, +} + +impl Trie { + fn new() -> Self { + Trie { + children: Default::default(), + is_end: false, + } + } + + fn insert(&mut self, w: &str) { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); + } + node = node.children[idx].as_mut().unwrap(); + } + node.is_end = true; + } + + fn search(&self, w: &str) -> bool { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() || !node.children[idx].as_ref().unwrap().is_end { + return false; } - if is_pass { - return word.clone(); + node = node.children[idx].as_ref().unwrap(); + } + true + } +} + +impl Solution { + pub fn longest_word(words: Vec) -> String { + let mut trie = Trie::new(); + for w in &words { + trie.insert(w); + } + + let mut ans = String::new(); + for w in words { + if trie.search(&w) && (ans.len() < w.len() || (ans.len() == w.len() && w < ans)) { + ans = w; } } - String::new() + ans } } ``` - +#### JavaScript + +```js +/** + * @param {string[]} words + * @return {string} + */ +var longestWord = function (words) { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } - + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; + } + } + return ans; +}; - +class Trie { + constructor() { + this.children = Array(26).fill(null); + this.isEnd = false; + } -### 方法二:排序 + insert(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + } + node.isEnd = true; + } + + search(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx].isEnd) { + return false; + } + node = node.children[idx]; + } + return true; + } +} +``` -优先返回符合条件、**长度最长且字典序最小**的单词,那么可以进行依照该规则,先对 `words` 进行排序,免去多个结果之间的比较。 + diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/README_EN.md b/solution/0700-0799/0720.Longest Word in Dictionary/README_EN.md index 886cf6301b6d7..83a5493fded69 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/README_EN.md +++ b/solution/0700-0799/0720.Longest Word in Dictionary/README_EN.md @@ -58,119 +58,202 @@ tags: -### Solution 1 +### Solution 1: Trie + +We can use a trie to store all the words, then traverse all the words to determine if the current word can be formed by adding one letter at a time from other words in the trie. Find the longest word that meets the condition and has the smallest lexicographical order. + +The time complexity is $O(L)$, and the space complexity is $O(L)$, where $L$ is the sum of the lengths of all words. #### Python3 ```python +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + self.is_end = False + + def insert(self, w: str): + 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 + + def search(self, w: str) -> bool: + node = self + for c in w: + idx = ord(c) - ord("a") + if node.children[idx] is None: + return False + node = node.children[idx] + if not node.is_end: + return False + return True + + class Solution: def longestWord(self, words: List[str]) -> str: - cnt, ans = 0, '' - s = set(words) - for w in s: - n = len(w) - if all(w[:i] in s for i in range(1, n)): - if cnt < n: - cnt, ans = n, w - elif cnt == n and w < ans: - ans = w + trie = Trie() + for w in words: + trie.insert(w) + ans = "" + for w in words: + if trie.search(w) and ( + len(ans) < len(w) or (len(ans) == len(w) and ans > w) + ): + ans = w return ans ``` #### Java ```java -class Solution { - private Set s; - - public String longestWord(String[] words) { - s = new HashSet<>(Arrays.asList(words)); - int cnt = 0; - String ans = ""; - for (String w : s) { - int n = w.length(); - if (check(w)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w.compareTo(ans) < 0) { - ans = w; - } +class Trie { + private Trie[] children = new Trie[26]; + private boolean isEnd = false; + + public void insert(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; } - return ans; + node.isEnd = true; } - private boolean check(String word) { - for (int i = 1, n = word.length(); i < n; ++i) { - if (!s.contains(word.substring(0, i))) { + public boolean search(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null || !node.children[idx].isEnd) { return false; } + node = node.children[idx]; } return true; } } + +class Solution { + public String longestWord(String[] words) { + Trie trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + String ans = ""; + for (String w : words) { + if (trie.search(w) + && (ans.length() < w.length() + || (ans.length() == w.length() && w.compareTo(ans) < 0))) { + ans = w; + } + } + return ans; + } +} ``` #### C++ ```cpp -class Solution { +class Trie { public: - string longestWord(vector& words) { - unordered_set s(words.begin(), words.end()); - int cnt = 0; - string ans = ""; - for (auto w : s) { - int n = w.size(); - if (check(w, s)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w < ans) - ans = w; + Trie* children[26] = {nullptr}; + bool isEnd = false; + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); } + node = node->children[idx]; } - return ans; + node->isEnd = true; } - bool check(string& word, unordered_set& s) { - for (int i = 1, n = word.size(); i < n; ++i) - if (!s.count(word.substr(0, i))) + bool search(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr || !node->children[idx]->isEnd) { return false; + } + node = node->children[idx]; + } return true; } }; + +class Solution { +public: + string longestWord(vector& words) { + Trie trie; + for (const string& w : words) { + trie.insert(w); + } + + string ans = ""; + for (const string& w : words) { + if (trie.search(w) && (ans.length() < w.length() || (ans.length() == w.length() && w < ans))) { + ans = w; + } + } + return ans; + } +}; ``` #### Go ```go +type Trie struct { + children [26]*Trie + isEnd bool +} + +func (t *Trie) insert(w string) { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } + node.isEnd = true +} + +func (t *Trie) search(w string) bool { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil || !node.children[idx].isEnd { + return false + } + node = node.children[idx] + } + return true +} + func longestWord(words []string) string { - s := make(map[string]bool) + trie := &Trie{} for _, w := range words { - s[w] = true + trie.insert(w) } - cnt := 0 + ans := "" - check := func(word string) bool { - for i, n := 1, len(word); i < n; i++ { - if !s[word[:i]] { - return false - } - } - return true - } - for w, _ := range s { - n := len(w) - if check(w) { - if cnt < n { - cnt, ans = n, w - } else if cnt == n && w < ans { - ans = w - } + for _, w := range words { + if trie.search(w) && (len(ans) < len(w) || (len(ans) == len(w) && w < ans)) { + ans = w } } return ans @@ -180,50 +263,160 @@ func longestWord(words []string) string { #### TypeScript ```ts -function longestWord(words: string[]): string { - words.sort((a, b) => { - const n = a.length; - const m = b.length; - if (n === m) { - return a < b ? -1 : 1; - } - return m - n; - }); - for (const word of words) { - let isPass = true; - for (let i = 1; i <= word.length; i++) { - if (!words.includes(word.slice(0, i))) { - isPass = false; - break; +class Trie { + children: (Trie | null)[] = new Array(26).fill(null); + isEnd: boolean = false; + + insert(w: string): void { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); } + node = node.children[idx]!; } - if (isPass) { - return word; + node.isEnd = true; + } + + search(w: string): boolean { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx]!.isEnd) { + return false; + } + node = node.children[idx]!; } + return true; } - return ''; +} + +function longestWord(words: string[]): string { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; + } + } + return ans; } ``` #### Rust ```rust +struct Trie { + children: [Option>; 26], + is_end: bool, +} + +impl Trie { + fn new() -> Self { + Trie { + children: Default::default(), + is_end: false, + } + } + + fn insert(&mut self, w: &str) { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); + } + node = node.children[idx].as_mut().unwrap(); + } + node.is_end = true; + } + + fn search(&self, w: &str) -> bool { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() || !node.children[idx].as_ref().unwrap().is_end { + return false; + } + node = node.children[idx].as_ref().unwrap(); + } + true + } +} + impl Solution { - pub fn longest_word(mut words: Vec) -> String { - words.sort_unstable_by(|a, b| (b.len(), a).cmp(&(a.len(), b))); - for word in words.iter() { - let mut is_pass = true; - for i in 1..=word.len() { - if !words.contains(&word[..i].to_string()) { - is_pass = false; - break; - } + pub fn longest_word(words: Vec) -> String { + let mut trie = Trie::new(); + for w in &words { + trie.insert(w); + } + + let mut ans = String::new(); + for w in words { + if trie.search(&w) && (ans.len() < w.len() || (ans.len() == w.len() && w < ans)) { + ans = w; } - if is_pass { - return word.clone(); + } + ans + } +} +``` + +#### JavaScript + +```js +/** + * @param {string[]} words + * @return {string} + */ +var longestWord = function (words) { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; + } + } + return ans; +}; + +class Trie { + constructor() { + this.children = Array(26).fill(null); + this.isEnd = false; + } + + insert(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; } - String::new() + node.isEnd = true; + } + + search(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx].isEnd) { + return false; + } + node = node.children[idx]; + } + return true; } } ``` diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.cpp b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.cpp index 49ea37bbdd0b2..8746bd16bc959 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.cpp +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.cpp @@ -1,26 +1,47 @@ -class Solution { +class Trie { public: - string longestWord(vector& words) { - unordered_set s(words.begin(), words.end()); - int cnt = 0; - string ans = ""; - for (auto w : s) { - int n = w.size(); - if (check(w, s)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w < ans) - ans = w; + Trie* children[26] = {nullptr}; + bool isEnd = false; + + void insert(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr) { + node->children[idx] = new Trie(); } + node = node->children[idx]; } - return ans; + node->isEnd = true; } - bool check(string& word, unordered_set& s) { - for (int i = 1, n = word.size(); i < n; ++i) - if (!s.count(word.substr(0, i))) + bool search(const string& w) { + Trie* node = this; + for (char c : w) { + int idx = c - 'a'; + if (node->children[idx] == nullptr || !node->children[idx]->isEnd) { return false; + } + node = node->children[idx]; + } return true; } -}; \ No newline at end of file +}; + +class Solution { +public: + string longestWord(vector& words) { + Trie trie; + for (const string& w : words) { + trie.insert(w); + } + + string ans = ""; + for (const string& w : words) { + if (trie.search(w) && (ans.length() < w.length() || (ans.length() == w.length() && w < ans))) { + ans = w; + } + } + return ans; + } +}; diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.go b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.go index 3cca4beb6f63b..d6d857180e6fd 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.go +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.go @@ -1,27 +1,43 @@ +type Trie struct { + children [26]*Trie + isEnd bool +} + +func (t *Trie) insert(w string) { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil { + node.children[idx] = &Trie{} + } + node = node.children[idx] + } + node.isEnd = true +} + +func (t *Trie) search(w string) bool { + node := t + for i := 0; i < len(w); i++ { + idx := w[i] - 'a' + if node.children[idx] == nil || !node.children[idx].isEnd { + return false + } + node = node.children[idx] + } + return true +} + func longestWord(words []string) string { - s := make(map[string]bool) + trie := &Trie{} for _, w := range words { - s[w] = true + trie.insert(w) } - cnt := 0 + ans := "" - check := func(word string) bool { - for i, n := 1, len(word); i < n; i++ { - if !s[word[:i]] { - return false - } - } - return true - } - for w, _ := range s { - n := len(w) - if check(w) { - if cnt < n { - cnt, ans = n, w - } else if cnt == n && w < ans { - ans = w - } + for _, w := range words { + if trie.search(w) && (len(ans) < len(w) || (len(ans) == len(w) && w < ans)) { + ans = w } } return ans -} \ No newline at end of file +} diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.java b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.java index 955f075a8dc2f..b0d44ba1e098a 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.java +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.java @@ -1,30 +1,46 @@ -class Solution { - private Set s; +class Trie { + private Trie[] children = new Trie[26]; + private boolean isEnd = false; - public String longestWord(String[] words) { - s = new HashSet<>(Arrays.asList(words)); - int cnt = 0; - String ans = ""; - for (String w : s) { - int n = w.length(); - if (check(w)) { - if (cnt < n) { - cnt = n; - ans = w; - } else if (cnt == n && w.compareTo(ans) < 0) { - ans = w; - } + public void insert(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new Trie(); } + node = node.children[idx]; } - return ans; + node.isEnd = true; } - private boolean check(String word) { - for (int i = 1, n = word.length(); i < n; ++i) { - if (!s.contains(word.substring(0, i))) { + public boolean search(String w) { + Trie node = this; + for (char c : w.toCharArray()) { + int idx = c - 'a'; + if (node.children[idx] == null || !node.children[idx].isEnd) { return false; } + node = node.children[idx]; } return true; } -} \ No newline at end of file +} + +class Solution { + public String longestWord(String[] words) { + Trie trie = new Trie(); + for (String w : words) { + trie.insert(w); + } + String ans = ""; + for (String w : words) { + if (trie.search(w) + && (ans.length() < w.length() + || (ans.length() == w.length() && w.compareTo(ans) < 0))) { + ans = w; + } + } + return ans; + } +} diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.js b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.js new file mode 100644 index 0000000000000..8f54c997eb696 --- /dev/null +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.js @@ -0,0 +1,49 @@ +/** + * @param {string[]} words + * @return {string} + */ +var longestWord = function (words) { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; + } + } + return ans; +}; + +class Trie { + constructor() { + this.children = Array(26).fill(null); + this.isEnd = false; + } + + insert(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); + } + node = node.children[idx]; + } + node.isEnd = true; + } + + search(w) { + let node = this; + for (let i = 0; i < w.length; i++) { + const idx = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx].isEnd) { + return false; + } + node = node.children[idx]; + } + return true; + } +} diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.py b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.py index bb9be70b2cfee..886a6430341b5 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.py +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.py @@ -1,12 +1,38 @@ +class Trie: + def __init__(self): + self.children: List[Optional[Trie]] = [None] * 26 + self.is_end = False + + def insert(self, w: str): + 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 + + def search(self, w: str) -> bool: + node = self + for c in w: + idx = ord(c) - ord("a") + if node.children[idx] is None: + return False + node = node.children[idx] + if not node.is_end: + return False + return True + + class Solution: def longestWord(self, words: List[str]) -> str: - cnt, ans = 0, '' - s = set(words) - for w in s: - n = len(w) - if all(w[:i] in s for i in range(1, n)): - if cnt < n: - cnt, ans = n, w - elif cnt == n and w < ans: - ans = w + trie = Trie() + for w in words: + trie.insert(w) + ans = "" + for w in words: + if trie.search(w) and ( + len(ans) < len(w) or (len(ans) == len(w) and ans > w) + ): + ans = w return ans diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.rs b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.rs index fc3c4673b768f..24091db61cb1f 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.rs +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.rs @@ -1,18 +1,54 @@ -impl Solution { - pub fn longest_word(mut words: Vec) -> String { - words.sort_unstable_by(|a, b| (b.len(), a).cmp(&(a.len(), b))); - for word in words.iter() { - let mut is_pass = true; - for i in 1..=word.len() { - if !words.contains(&word[..i].to_string()) { - is_pass = false; - break; - } +struct Trie { + children: [Option>; 26], + is_end: bool, +} + +impl Trie { + fn new() -> Self { + Trie { + children: Default::default(), + is_end: false, + } + } + + fn insert(&mut self, w: &str) { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() { + node.children[idx] = Some(Box::new(Trie::new())); + } + node = node.children[idx].as_mut().unwrap(); + } + node.is_end = true; + } + + fn search(&self, w: &str) -> bool { + let mut node = self; + for c in w.chars() { + let idx = (c as usize) - ('a' as usize); + if node.children[idx].is_none() || !node.children[idx].as_ref().unwrap().is_end { + return false; } - if is_pass { - return word.clone(); + node = node.children[idx].as_ref().unwrap(); + } + true + } +} + +impl Solution { + pub fn longest_word(words: Vec) -> String { + let mut trie = Trie::new(); + for w in &words { + trie.insert(w); + } + + let mut ans = String::new(); + for w in words { + if trie.search(&w) && (ans.len() < w.len() || (ans.len() == w.len() && w < ans)) { + ans = w; } } - String::new() + ans } } diff --git a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.ts b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.ts index 4c73b31ddc97d..6803cc5a46d21 100644 --- a/solution/0700-0799/0720.Longest Word in Dictionary/Solution.ts +++ b/solution/0700-0799/0720.Longest Word in Dictionary/Solution.ts @@ -1,23 +1,43 @@ -function longestWord(words: string[]): string { - words.sort((a, b) => { - const n = a.length; - const m = b.length; - if (n === m) { - return a < b ? -1 : 1; +class Trie { + children: (Trie | null)[] = new Array(26).fill(null); + isEnd: boolean = false; + + insert(w: string): void { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null) { + node.children[idx] = new Trie(); + } + node = node.children[idx]!; } - return m - n; - }); - for (const word of words) { - let isPass = true; - for (let i = 1; i <= word.length; i++) { - if (!words.includes(word.slice(0, i))) { - isPass = false; - break; + node.isEnd = true; + } + + search(w: string): boolean { + let node: Trie = this; + for (let i = 0; i < w.length; i++) { + const idx: number = w.charCodeAt(i) - 'a'.charCodeAt(0); + if (node.children[idx] === null || !node.children[idx]!.isEnd) { + return false; } + node = node.children[idx]!; } - if (isPass) { - return word; + return true; + } +} + +function longestWord(words: string[]): string { + const trie = new Trie(); + for (const w of words) { + trie.insert(w); + } + + let ans = ''; + for (const w of words) { + if (trie.search(w) && (ans.length < w.length || (ans.length === w.length && w < ans))) { + ans = w; } } - return ''; + return ans; } diff --git a/solution/0700-0799/0722.Remove Comments/README.md b/solution/0700-0799/0722.Remove Comments/README.md index 144b3f2264ef8..8fefc8317cb69 100644 --- a/solution/0700-0799/0722.Remove Comments/README.md +++ b/solution/0700-0799/0722.Remove Comments/README.md @@ -59,20 +59,20 @@ tags: 解释: 示例代码可以编排成这样: /*Test program */ int main() -{ - // variable declaration +{ + // variable declaration int a, b, c; /* This is a test - multiline - comment for + multiline + comment for testing */ a = b + c; } 第 1 行和第 6-9 行的字符串 /* 表示块注释。第 4 行的字符串 // 表示行注释。 -编排后: +编排后: int main() -{ - +{ + int a, b, c; a = b + c; } @@ -106,15 +106,15 @@ a = b + c; ### 方法一:分情况讨论 -我们用一个变量 $blockComment$ 来表示当前是否处于块注释中,初始时 $blockComment$ 为 `false`;用一个变量 $t$ 来存储当前行的有效字符。 +我们用一个变量 来表示当前是否处于块注释中,初始时 $\textit{blockComment}$ 为 `false`;用一个变量 $t$ 来存储当前行的有效字符。 接下来,遍历每一行,分情况讨论: -如果当前处于块注释中,那么如果当前字符和下一个字符是 `'*/'`,说明块注释结束,我们将 $blockComment$ 置为 `false`,并且跳过这两个字符;否则,我们继续保持块注释状态,不做任何操作; +如果当前处于块注释中,那么如果当前字符和下一个字符是 `'*/'`,说明块注释结束,我们将 $\textit{blockComment}$ 置为 `false`,并且跳过这两个字符;否则,我们继续保持块注释状态,不做任何操作; -如果当前不处于块注释中,那么如果当前字符和下一个字符是 `'/*'`,说明块注释开始,我们将 $blockComment$ 置为 `true`,并且跳过这两个字符;如果当前字符和下一个字符是 `'//'`,那么说明行注释开始,我们直接退出当前行的遍历;否则,说明当前字符是有效字符,我们将其加入 $t$ 中; +如果当前不处于块注释中,那么如果当前字符和下一个字符是 `'/*'`,说明块注释开始,我们将 $\textit{blockComment}$ 置为 `true`,并且跳过这两个字符;如果当前字符和下一个字符是 `'//'`,那么说明行注释开始,我们直接退出当前行的遍历;否则,说明当前字符是有效字符,我们将其加入 $t$ 中; -遍历完当前行后,如果 $blockComment$ 为 `false`,并且 $t$ 不为空,说明当前行是有效行,我们将其加入答案数组中,并且清空 $t$。继续遍历下一行。 +遍历完当前行后,如果 $\textit{blockComment}$ 为 `false`,并且 $t$ 不为空,说明当前行是有效行,我们将其加入答案数组中,并且清空 $t$。继续遍历下一行。 时间复杂度 $O(L)$,空间复杂度 $O(L)$,其中 $L$ 是源代码的总长度。 diff --git a/solution/0700-0799/0722.Remove Comments/README_EN.md b/solution/0700-0799/0722.Remove Comments/README_EN.md index 9d1a77e0b19a7..bbb95fdbaccea 100644 --- a/solution/0700-0799/0722.Remove Comments/README_EN.md +++ b/solution/0700-0799/0722.Remove Comments/README_EN.md @@ -58,20 +58,20 @@ tags: Explanation: The line by line code is visualized as below: /*Test program */ int main() -{ - // variable declaration +{ + // variable declaration int a, b, c; /* This is a test - multiline - comment for + multiline + comment for testing */ a = b + c; } The string /* denotes a block comment, including line 1 and lines 6-9. The string // denotes line 4 as comments. The line by line output code is visualized as below: int main() -{ - +{ + int a, b, c; a = b + c; } @@ -102,7 +102,19 @@ a = b + c; -### Solution 1 +### Solution 1: Case Analysis + +We use a variable $\textit{blockComment}$ to indicate whether we are currently in a block comment. Initially, $\textit{blockComment}$ is `false`. We use a variable $t$ to store the valid characters of the current line. + +Next, we traverse each line and discuss the following cases: + +If we are currently in a block comment, and the current character and the next character are `'*/'`, it means the block comment ends. We set $\textit{blockComment}$ to `false` and skip these two characters. Otherwise, we continue in the block comment state without doing anything. + +If we are not currently in a block comment, and the current character and the next character are `'/*'`, it means a block comment starts. We set $\textit{blockComment}$ to `true` and skip these two characters. If the current character and the next character are `'//'`, it means a line comment starts, and we exit the current line traversal. Otherwise, the current character is a valid character, and we add it to $t$. + +After traversing the current line, if $\textit{blockComment}$ is `false` and $t$ is not empty, it means the current line is valid. We add it to the answer array and clear $t$. Continue to traverse the next line. + +The time complexity is $O(L)$, and the space complexity is $O(L)$, where $L$ is the total length of the source code. diff --git a/solution/0700-0799/0727.Minimum Window Subsequence/README.md b/solution/0700-0799/0727.Minimum Window Subsequence/README.md index 3d34c288122be..f833e9bb1ec9d 100644 --- a/solution/0700-0799/0727.Minimum Window Subsequence/README.md +++ b/solution/0700-0799/0727.Minimum Window Subsequence/README.md @@ -60,7 +60,7 @@ s1 = "abcdebdde", s2 = "bde" ### 方法一:动态规划 -我们定义 $f[i][j]$ 表示字符串 $s1$ 的前 $i$ 个字符包含字符串 $s2$ 的前 $j$ 个字符时的最短子串的起始位置,如果不存在则为 $0$。 +我们定义 $f[i][j]$ 表示字符串 $\textit{s1}$ 的前 $i$ 个字符包含字符串 $\textit{s2}$ 的前 $j$ 个字符时的最短子串的起始位置,如果不存在则为 $0$。 我们可以得到状态转移方程: @@ -72,9 +72,9 @@ f[i - 1][j], & s1[i-1] \ne s2[j-1] \end{cases} $$ -接下来我们只需要遍历 $s1$,如果 $f[i][n] \gt 0$,则更新最短子串的起始位置和长度。最后返回最短子串即可。 +接下来我们只需要遍历 $\textit{s1}$,如果 $f[i][n] \gt 0$,则更新最短子串的起始位置和长度。最后返回最短子串即可。 -时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为字符串 $s1$ 和 $s2$ 的长度。 +时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为字符串 $\textit{s1}$ 和 $\textit{s2}$ 的长度。 diff --git a/solution/0700-0799/0727.Minimum Window Subsequence/README_EN.md b/solution/0700-0799/0727.Minimum Window Subsequence/README_EN.md index 844bccf6cddef..5607e7c495e0b 100644 --- a/solution/0700-0799/0727.Minimum Window Subsequence/README_EN.md +++ b/solution/0700-0799/0727.Minimum Window Subsequence/README_EN.md @@ -28,7 +28,7 @@ tags:
 Input: s1 = "abcdebdde", s2 = "bde"
 Output: "bcde"
-Explanation: 
+Explanation:
 "bcde" is the answer because it occurs before "bdde" which has the same length.
 "deb" is not a smaller window because the elements of s2 in the window must occur in order.
 
@@ -55,7 +55,23 @@ tags: -### Solution 1 +### Solution 1: Dynamic Programming + +We define $f[i][j]$ to represent the starting position of the shortest substring of the first $i$ characters of string $\textit{s1}$ that contains the first $j$ characters of string $\textit{s2}$. If it does not exist, it is $0$. + +We can derive the state transition equation: + +$$ +f[i][j] = \begin{cases} +i, & j = 1 \textit{ and } s1[i-1] = s2[j] \\ +f[i - 1][j - 1], & j > 1 \textit{ and } s1[i-1] = s2[j-1] \\ +f[i - 1][j], & s1[i-1] \ne s2[j-1] +\end{cases} +$$ + +Next, we only need to traverse $\textit{s1}$. If $f[i][n] \gt 0$, update the starting position and length of the shortest substring. Finally, return the shortest substring. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Here, $m$ and $n$ are the lengths of strings $\textit{s1}$ and $\textit{s2}$, respectively. diff --git a/solution/0700-0799/0733.Flood Fill/README.md b/solution/0700-0799/0733.Flood Fill/README.md index 9cbac1e653076..d7df13a582bc2 100644 --- a/solution/0700-0799/0733.Flood Fill/README.md +++ b/solution/0700-0799/0733.Flood Fill/README.md @@ -75,13 +75,11 @@ tags: -### 方法一:Flood fill 算法 +### 方法一:DFS -Flood fill 算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名。 +我们记初始像素的颜色为 $\textit{oc}$,如果 $\textit{oc}$ 不等于目标颜色 $\textit{color}$,我们就从 $(\textit{sr}, \textit{sc})$ 开始深度优先搜索,将所有符合条件的像素点的颜色都更改成目标颜色。 -最简单的实现方法是采用 DFS 的递归方法,也可以采用 BFS 的迭代来实现。 - -时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为图像的行数和列数。 +时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为二维数组 $\textit{image}$ 的行数和列数。 @@ -92,22 +90,17 @@ class Solution: def floodFill( self, image: List[List[int]], sr: int, sc: int, color: int ) -> List[List[int]]: - def dfs(i, j): - if ( - not 0 <= i < m - or not 0 <= j < n - or image[i][j] != oc - or image[i][j] == color - ): - return + def dfs(i: int, j: int): image[i][j] = color for a, b in pairwise(dirs): - dfs(i + a, j + b) + x, y = i + a, j + b + if 0 <= x < len(image) and 0 <= y < len(image[0]) and image[x][y] == oc: + dfs(x, y) - dirs = (-1, 0, 1, 0, -1) - m, n = len(image), len(image[0]) oc = image[sr][sc] - dfs(sr, sc) + if oc != color: + dirs = (-1, 0, 1, 0, -1) + dfs(sr, sc) return image ``` @@ -115,27 +108,29 @@ class Solution: ```java class Solution { - private int[] dirs = {-1, 0, 1, 0, -1}; private int[][] image; - private int nc; private int oc; + private int color; + private final int[] dirs = {-1, 0, 1, 0, -1}; public int[][] floodFill(int[][] image, int sr, int sc, int color) { - nc = color; oc = image[sr][sc]; + if (oc == color) { + return image; + } this.image = image; + this.color = color; dfs(sr, sc); return image; } private void dfs(int i, int j) { - if (i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oc - || image[i][j] == nc) { - return; - } - image[i][j] = nc; + image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < image.length && y >= 0 && y < image[0].length && image[x][y] == oc) { + dfs(x, y); + } } } } @@ -149,14 +144,17 @@ public: vector> floodFill(vector>& image, int sr, int sc, int color) { int m = image.size(), n = image[0].size(); int oc = image[sr][sc]; - int dirs[5] = {-1, 0, 1, 0, -1}; - function dfs = [&](int i, int j) { - if (i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color) { - return; - } + if (oc == color) { + return image; + } + const int dirs[5] = {-1, 0, 1, 0, -1}; + auto dfs = [&](this auto&& dfs, int i, int j) -> void { image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc) { + dfs(x, y); + } } }; dfs(sr, sc); @@ -169,19 +167,25 @@ public: ```go func floodFill(image [][]int, sr int, sc int, color int) [][]int { - oc := image[sr][sc] m, n := len(image), len(image[0]) + oc := image[sr][sc] + if oc == color { + return image + } + dirs := []int{-1, 0, 1, 0, -1} + var dfs func(i, j int) dfs = func(i, j int) { - if i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color { - return - } image[i][j] = color for k := 0; k < 4; k++ { - dfs(i+dirs[k], j+dirs[k+1]) + x, y := i+dirs[k], j+dirs[k+1] + if x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc { + dfs(x, y) + } } } + dfs(sr, sc) return image } @@ -190,27 +194,25 @@ func floodFill(image [][]int, sr int, sc int, color int) [][]int { #### TypeScript ```ts -function floodFill(image: number[][], sr: number, sc: number, newColor: number): number[][] { - const m = image.length; - const n = image[0].length; - const target = image[sr][sc]; - const dfs = (i: number, j: number) => { - if ( - i < 0 || - i === m || - j < 0 || - j === n || - image[i][j] !== target || - image[i][j] === newColor - ) { - return; +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + const [m, n] = [image.length, image[0].length]; + const oc = image[sr][sc]; + if (oc === color) { + return image; + } + + const dirs = [-1, 0, 1, 0, -1]; + + const dfs = (i: number, j: number): void => { + image[i][j] = color; + for (let k = 0; k < 4; k++) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + dfs(x, y); + } } - image[i][j] = newColor; - dfs(i + 1, j); - dfs(i - 1, j); - dfs(i, j + 1); - dfs(i, j - 1); }; + dfs(sr, sc); return image; } @@ -220,26 +222,42 @@ function floodFill(image: number[][], sr: number, sc: number, newColor: number): ```rust impl Solution { - fn dfs(image: &mut Vec>, sr: i32, sc: i32, new_color: i32, target: i32) { - if sr < 0 || sr == (image.len() as i32) || sc < 0 || sc == (image[0].len() as i32) { - return; - } + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); let sr = sr as usize; let sc = sc as usize; - if sr < 0 || image[sr][sc] == new_color || image[sr][sc] != target { - return; + + let oc = image[sr][sc]; + if oc == color { + return image; } - image[sr][sc] = new_color; - let sr = sr as i32; - let sc = sc as i32; - Self::dfs(image, sr + 1, sc, new_color, target); - Self::dfs(image, sr - 1, sc, new_color, target); - Self::dfs(image, sr, sc + 1, new_color, target); - Self::dfs(image, sr, sc - 1, new_color, target); - } - pub fn flood_fill(image: Vec>, sr: i32, sc: i32, new_color: i32) -> Vec> { - let target = image[sr as usize][sc as usize]; - Self::dfs(&mut image, sr, sc, new_color, target); + let dirs = [-1, 0, 1, 0, -1]; + fn dfs( + image: &mut Vec>, + i: usize, + j: usize, + oc: i32, + color: i32, + m: usize, + n: usize, + dirs: &[i32; 5], + ) { + image[i][j] = color; + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let x = x as usize; + let y = y as usize; + if image[x][y] == oc { + dfs(image, x, y, oc, color, m, n, dirs); + } + } + } + } + + dfs(&mut image, sr, sc, oc, color, m, n, &dirs); image } } @@ -251,7 +269,13 @@ impl Solution { -### 方法二 +### 方法二:BFS + +我们首先判断初始像素的颜色是否等于目标颜色,如果等于,直接返回原图像。否则,我们可以使用广度优先搜索的方法,从 $(\textit{sr}, \textit{sc})$ 开始,将所有符合条件的像素点的颜色都更改成目标颜色。 + +具体地,我们定义一个队列 $\textit{q}$,将初始像素 $(\textit{sr}, \textit{sc})$ 加入队列。然后我们不断从队列中取出像素点 $(i, j)$,将其颜色更改成目标颜色,并将其上下左右四个方向上与初始像素的原始颜色相同的像素点加入队列。直到队列为空,我们就完成了图像的渲染。 + +时间复杂度 $O(m \times n)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别为二维数组 $\textit{image}$ 的行数和列数。 @@ -363,6 +387,82 @@ func floodFill(image [][]int, sr int, sc int, color int) [][]int { } ``` +#### TypeScript + +```ts +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + if (image[sr][sc] === color) { + return image; + } + + const oc = image[sr][sc]; + image[sr][sc] = color; + + const q: [number, number][] = []; + q.push([sr, sc]); + + const dirs = [-1, 0, 1, 0, -1]; + const [m, n] = [image.length, image[0].length]; + + while (q.length > 0) { + const [a, b] = q.shift()!; + for (let k = 0; k < 4; ++k) { + const x = a + dirs[k]; + const y = b + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + q.push([x, y]); + image[x][y] = color; + } + } + } + + return image; +} +``` + +#### Rust + +```rust +use std::collections::VecDeque; + +impl Solution { + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); + let (sr, sc) = (sr as usize, sc as usize); + + if image[sr][sc] == color { + return image; + } + + let oc = image[sr][sc]; + image[sr][sc] = color; + + let mut q = VecDeque::new(); + q.push_back((sr, sc)); + + let dirs = [-1, 0, 1, 0, -1]; + + while let Some((i, j)) = q.pop_front() { + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let (x, y) = (x as usize, y as usize); + if image[x][y] == oc { + q.push_back((x, y)); + image[x][y] = color; + } + } + } + } + + image + } +} +``` + diff --git a/solution/0700-0799/0733.Flood Fill/README_EN.md b/solution/0700-0799/0733.Flood Fill/README_EN.md index 695663dd6381b..da347008f37fc 100644 --- a/solution/0700-0799/0733.Flood Fill/README_EN.md +++ b/solution/0700-0799/0733.Flood Fill/README_EN.md @@ -79,7 +79,11 @@ tags: -### Solution 1 +### Solution 1: DFS + +We denote the initial pixel's color as $\textit{oc}$. If $\textit{oc}$ is not equal to the target color $\textit{color}$, we start a depth-first search from $(\textit{sr}, \textit{sc})$ to change the color of all eligible pixels to the target color. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Here, $m$ and $n$ are the number of rows and columns of the 2D array $\textit{image}$, respectively. @@ -90,22 +94,17 @@ class Solution: def floodFill( self, image: List[List[int]], sr: int, sc: int, color: int ) -> List[List[int]]: - def dfs(i, j): - if ( - not 0 <= i < m - or not 0 <= j < n - or image[i][j] != oc - or image[i][j] == color - ): - return + def dfs(i: int, j: int): image[i][j] = color for a, b in pairwise(dirs): - dfs(i + a, j + b) + x, y = i + a, j + b + if 0 <= x < len(image) and 0 <= y < len(image[0]) and image[x][y] == oc: + dfs(x, y) - dirs = (-1, 0, 1, 0, -1) - m, n = len(image), len(image[0]) oc = image[sr][sc] - dfs(sr, sc) + if oc != color: + dirs = (-1, 0, 1, 0, -1) + dfs(sr, sc) return image ``` @@ -113,27 +112,29 @@ class Solution: ```java class Solution { - private int[] dirs = {-1, 0, 1, 0, -1}; private int[][] image; - private int nc; private int oc; + private int color; + private final int[] dirs = {-1, 0, 1, 0, -1}; public int[][] floodFill(int[][] image, int sr, int sc, int color) { - nc = color; oc = image[sr][sc]; + if (oc == color) { + return image; + } this.image = image; + this.color = color; dfs(sr, sc); return image; } private void dfs(int i, int j) { - if (i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oc - || image[i][j] == nc) { - return; - } - image[i][j] = nc; + image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < image.length && y >= 0 && y < image[0].length && image[x][y] == oc) { + dfs(x, y); + } } } } @@ -147,14 +148,17 @@ public: vector> floodFill(vector>& image, int sr, int sc, int color) { int m = image.size(), n = image[0].size(); int oc = image[sr][sc]; - int dirs[5] = {-1, 0, 1, 0, -1}; - function dfs = [&](int i, int j) { - if (i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color) { - return; - } + if (oc == color) { + return image; + } + const int dirs[5] = {-1, 0, 1, 0, -1}; + auto dfs = [&](this auto&& dfs, int i, int j) -> void { image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc) { + dfs(x, y); + } } }; dfs(sr, sc); @@ -167,19 +171,25 @@ public: ```go func floodFill(image [][]int, sr int, sc int, color int) [][]int { - oc := image[sr][sc] m, n := len(image), len(image[0]) + oc := image[sr][sc] + if oc == color { + return image + } + dirs := []int{-1, 0, 1, 0, -1} + var dfs func(i, j int) dfs = func(i, j int) { - if i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color { - return - } image[i][j] = color for k := 0; k < 4; k++ { - dfs(i+dirs[k], j+dirs[k+1]) + x, y := i+dirs[k], j+dirs[k+1] + if x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc { + dfs(x, y) + } } } + dfs(sr, sc) return image } @@ -188,27 +198,25 @@ func floodFill(image [][]int, sr int, sc int, color int) [][]int { #### TypeScript ```ts -function floodFill(image: number[][], sr: number, sc: number, newColor: number): number[][] { - const m = image.length; - const n = image[0].length; - const target = image[sr][sc]; - const dfs = (i: number, j: number) => { - if ( - i < 0 || - i === m || - j < 0 || - j === n || - image[i][j] !== target || - image[i][j] === newColor - ) { - return; +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + const [m, n] = [image.length, image[0].length]; + const oc = image[sr][sc]; + if (oc === color) { + return image; + } + + const dirs = [-1, 0, 1, 0, -1]; + + const dfs = (i: number, j: number): void => { + image[i][j] = color; + for (let k = 0; k < 4; k++) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + dfs(x, y); + } } - image[i][j] = newColor; - dfs(i + 1, j); - dfs(i - 1, j); - dfs(i, j + 1); - dfs(i, j - 1); }; + dfs(sr, sc); return image; } @@ -218,26 +226,42 @@ function floodFill(image: number[][], sr: number, sc: number, newColor: number): ```rust impl Solution { - fn dfs(image: &mut Vec>, sr: i32, sc: i32, new_color: i32, target: i32) { - if sr < 0 || sr == (image.len() as i32) || sc < 0 || sc == (image[0].len() as i32) { - return; - } + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); let sr = sr as usize; let sc = sc as usize; - if sr < 0 || image[sr][sc] == new_color || image[sr][sc] != target { - return; + + let oc = image[sr][sc]; + if oc == color { + return image; } - image[sr][sc] = new_color; - let sr = sr as i32; - let sc = sc as i32; - Self::dfs(image, sr + 1, sc, new_color, target); - Self::dfs(image, sr - 1, sc, new_color, target); - Self::dfs(image, sr, sc + 1, new_color, target); - Self::dfs(image, sr, sc - 1, new_color, target); - } - pub fn flood_fill(image: Vec>, sr: i32, sc: i32, new_color: i32) -> Vec> { - let target = image[sr as usize][sc as usize]; - Self::dfs(&mut image, sr, sc, new_color, target); + let dirs = [-1, 0, 1, 0, -1]; + fn dfs( + image: &mut Vec>, + i: usize, + j: usize, + oc: i32, + color: i32, + m: usize, + n: usize, + dirs: &[i32; 5], + ) { + image[i][j] = color; + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let x = x as usize; + let y = y as usize; + if image[x][y] == oc { + dfs(image, x, y, oc, color, m, n, dirs); + } + } + } + } + + dfs(&mut image, sr, sc, oc, color, m, n, &dirs); image } } @@ -249,7 +273,13 @@ impl Solution { -### Solution 2 +### Solution 2: BFS + +We first check if the initial pixel's color is equal to the target color. If it is, we return the original image directly. Otherwise, we can use the breadth-first search method, starting from $(\textit{sr}, \textit{sc})$, to change the color of all eligible pixels to the target color. + +Specifically, we define a queue $\textit{q}$ and add the initial pixel $(\textit{sr}, \textit{sc})$ to the queue. Then, we continuously take pixels $(i, j)$ from the queue, change their color to the target color, and add the pixels in the four directions (up, down, left, right) that have the same original color as the initial pixel to the queue. When the queue is empty, we have completed the flood fill. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Here, $m$ and $n$ are the number of rows and columns of the 2D array $\textit{image}$, respectively. @@ -361,6 +391,82 @@ func floodFill(image [][]int, sr int, sc int, color int) [][]int { } ``` +#### TypeScript + +```ts +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + if (image[sr][sc] === color) { + return image; + } + + const oc = image[sr][sc]; + image[sr][sc] = color; + + const q: [number, number][] = []; + q.push([sr, sc]); + + const dirs = [-1, 0, 1, 0, -1]; + const [m, n] = [image.length, image[0].length]; + + while (q.length > 0) { + const [a, b] = q.shift()!; + for (let k = 0; k < 4; ++k) { + const x = a + dirs[k]; + const y = b + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + q.push([x, y]); + image[x][y] = color; + } + } + } + + return image; +} +``` + +#### Rust + +```rust +use std::collections::VecDeque; + +impl Solution { + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); + let (sr, sc) = (sr as usize, sc as usize); + + if image[sr][sc] == color { + return image; + } + + let oc = image[sr][sc]; + image[sr][sc] = color; + + let mut q = VecDeque::new(); + q.push_back((sr, sc)); + + let dirs = [-1, 0, 1, 0, -1]; + + while let Some((i, j)) = q.pop_front() { + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let (x, y) = (x as usize, y as usize); + if image[x][y] == oc { + q.push_back((x, y)); + image[x][y] = color; + } + } + } + } + + image + } +} +``` + diff --git a/solution/0700-0799/0733.Flood Fill/Solution.cpp b/solution/0700-0799/0733.Flood Fill/Solution.cpp index 159a549768809..d56f7b5fa156b 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.cpp +++ b/solution/0700-0799/0733.Flood Fill/Solution.cpp @@ -3,17 +3,20 @@ class Solution { vector> floodFill(vector>& image, int sr, int sc, int color) { int m = image.size(), n = image[0].size(); int oc = image[sr][sc]; - int dirs[5] = {-1, 0, 1, 0, -1}; - function dfs = [&](int i, int j) { - if (i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color) { - return; - } + if (oc == color) { + return image; + } + const int dirs[5] = {-1, 0, 1, 0, -1}; + auto dfs = [&](this auto&& dfs, int i, int j) -> void { image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc) { + dfs(x, y); + } } }; dfs(sr, sc); return image; } -}; \ No newline at end of file +}; diff --git a/solution/0700-0799/0733.Flood Fill/Solution.go b/solution/0700-0799/0733.Flood Fill/Solution.go index 745e95d8ed566..e984a6a7c7bb6 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.go +++ b/solution/0700-0799/0733.Flood Fill/Solution.go @@ -1,17 +1,23 @@ func floodFill(image [][]int, sr int, sc int, color int) [][]int { - oc := image[sr][sc] m, n := len(image), len(image[0]) + oc := image[sr][sc] + if oc == color { + return image + } + dirs := []int{-1, 0, 1, 0, -1} + var dfs func(i, j int) dfs = func(i, j int) { - if i < 0 || i >= m || j < 0 || j >= n || image[i][j] != oc || image[i][j] == color { - return - } image[i][j] = color for k := 0; k < 4; k++ { - dfs(i+dirs[k], j+dirs[k+1]) + x, y := i+dirs[k], j+dirs[k+1] + if x >= 0 && x < m && y >= 0 && y < n && image[x][y] == oc { + dfs(x, y) + } } } + dfs(sr, sc) return image -} \ No newline at end of file +} diff --git a/solution/0700-0799/0733.Flood Fill/Solution.java b/solution/0700-0799/0733.Flood Fill/Solution.java index a8584e35c9e40..a932aa6f77a8e 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.java +++ b/solution/0700-0799/0733.Flood Fill/Solution.java @@ -1,25 +1,27 @@ class Solution { - private int[] dirs = {-1, 0, 1, 0, -1}; private int[][] image; - private int nc; private int oc; + private int color; + private final int[] dirs = {-1, 0, 1, 0, -1}; public int[][] floodFill(int[][] image, int sr, int sc, int color) { - nc = color; oc = image[sr][sc]; + if (oc == color) { + return image; + } this.image = image; + this.color = color; dfs(sr, sc); return image; } private void dfs(int i, int j) { - if (i < 0 || i >= image.length || j < 0 || j >= image[0].length || image[i][j] != oc - || image[i][j] == nc) { - return; - } - image[i][j] = nc; + image[i][j] = color; for (int k = 0; k < 4; ++k) { - dfs(i + dirs[k], j + dirs[k + 1]); + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < image.length && y >= 0 && y < image[0].length && image[x][y] == oc) { + dfs(x, y); + } } } -} \ No newline at end of file +} diff --git a/solution/0700-0799/0733.Flood Fill/Solution.py b/solution/0700-0799/0733.Flood Fill/Solution.py index 0c1e525a64c58..59f0414d33a6f 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.py +++ b/solution/0700-0799/0733.Flood Fill/Solution.py @@ -2,20 +2,15 @@ class Solution: def floodFill( self, image: List[List[int]], sr: int, sc: int, color: int ) -> List[List[int]]: - def dfs(i, j): - if ( - not 0 <= i < m - or not 0 <= j < n - or image[i][j] != oc - or image[i][j] == color - ): - return + def dfs(i: int, j: int): image[i][j] = color for a, b in pairwise(dirs): - dfs(i + a, j + b) + x, y = i + a, j + b + if 0 <= x < len(image) and 0 <= y < len(image[0]) and image[x][y] == oc: + dfs(x, y) - dirs = (-1, 0, 1, 0, -1) - m, n = len(image), len(image[0]) oc = image[sr][sc] - dfs(sr, sc) + if oc != color: + dirs = (-1, 0, 1, 0, -1) + dfs(sr, sc) return image diff --git a/solution/0700-0799/0733.Flood Fill/Solution.rs b/solution/0700-0799/0733.Flood Fill/Solution.rs index 81a4f802a623f..646e7bf4f3e76 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.rs +++ b/solution/0700-0799/0733.Flood Fill/Solution.rs @@ -1,24 +1,40 @@ impl Solution { - fn dfs(image: &mut Vec>, sr: i32, sc: i32, new_color: i32, target: i32) { - if sr < 0 || sr == (image.len() as i32) || sc < 0 || sc == (image[0].len() as i32) { - return; - } + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); let sr = sr as usize; let sc = sc as usize; - if sr < 0 || image[sr][sc] == new_color || image[sr][sc] != target { - return; + + let oc = image[sr][sc]; + if oc == color { + return image; } - image[sr][sc] = new_color; - let sr = sr as i32; - let sc = sc as i32; - Self::dfs(image, sr + 1, sc, new_color, target); - Self::dfs(image, sr - 1, sc, new_color, target); - Self::dfs(image, sr, sc + 1, new_color, target); - Self::dfs(image, sr, sc - 1, new_color, target); - } - pub fn flood_fill(image: Vec>, sr: i32, sc: i32, new_color: i32) -> Vec> { - let target = image[sr as usize][sc as usize]; - Self::dfs(&mut image, sr, sc, new_color, target); + let dirs = [-1, 0, 1, 0, -1]; + fn dfs( + image: &mut Vec>, + i: usize, + j: usize, + oc: i32, + color: i32, + m: usize, + n: usize, + dirs: &[i32; 5], + ) { + image[i][j] = color; + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let x = x as usize; + let y = y as usize; + if image[x][y] == oc { + dfs(image, x, y, oc, color, m, n, dirs); + } + } + } + } + + dfs(&mut image, sr, sc, oc, color, m, n, &dirs); image } } diff --git a/solution/0700-0799/0733.Flood Fill/Solution.ts b/solution/0700-0799/0733.Flood Fill/Solution.ts index cff3110f7ba91..01e6b5771a132 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution.ts +++ b/solution/0700-0799/0733.Flood Fill/Solution.ts @@ -1,24 +1,22 @@ -function floodFill(image: number[][], sr: number, sc: number, newColor: number): number[][] { - const m = image.length; - const n = image[0].length; - const target = image[sr][sc]; - const dfs = (i: number, j: number) => { - if ( - i < 0 || - i === m || - j < 0 || - j === n || - image[i][j] !== target || - image[i][j] === newColor - ) { - return; +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + const [m, n] = [image.length, image[0].length]; + const oc = image[sr][sc]; + if (oc === color) { + return image; + } + + const dirs = [-1, 0, 1, 0, -1]; + + const dfs = (i: number, j: number): void => { + image[i][j] = color; + for (let k = 0; k < 4; k++) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + dfs(x, y); + } } - image[i][j] = newColor; - dfs(i + 1, j); - dfs(i - 1, j); - dfs(i, j + 1); - dfs(i, j - 1); }; + dfs(sr, sc); return image; } diff --git a/solution/0700-0799/0733.Flood Fill/Solution2.cpp b/solution/0700-0799/0733.Flood Fill/Solution2.cpp index 2425f5a5e6722..3767abe005cd0 100644 --- a/solution/0700-0799/0733.Flood Fill/Solution2.cpp +++ b/solution/0700-0799/0733.Flood Fill/Solution2.cpp @@ -1,7 +1,9 @@ class Solution { public: vector> floodFill(vector>& image, int sr, int sc, int color) { - if (image[sr][sc] == color) return image; + if (image[sr][sc] == color) { + return image; + } int oc = image[sr][sc]; image[sr][sc] = color; queue> q; @@ -21,4 +23,4 @@ class Solution { } return image; } -}; \ No newline at end of file +}; diff --git a/solution/0700-0799/0733.Flood Fill/Solution2.rs b/solution/0700-0799/0733.Flood Fill/Solution2.rs new file mode 100644 index 0000000000000..0b86fe472095d --- /dev/null +++ b/solution/0700-0799/0733.Flood Fill/Solution2.rs @@ -0,0 +1,38 @@ +use std::collections::VecDeque; + +impl Solution { + pub fn flood_fill(mut image: Vec>, sr: i32, sc: i32, color: i32) -> Vec> { + let m = image.len(); + let n = image[0].len(); + let (sr, sc) = (sr as usize, sc as usize); + + if image[sr][sc] == color { + return image; + } + + let oc = image[sr][sc]; + image[sr][sc] = color; + + let mut q = VecDeque::new(); + q.push_back((sr, sc)); + + let dirs = [-1, 0, 1, 0, -1]; + + while let Some((i, j)) = q.pop_front() { + for k in 0..4 { + let x = i as isize + dirs[k] as isize; + let y = j as isize + dirs[k + 1] as isize; + + if x >= 0 && x < m as isize && y >= 0 && y < n as isize { + let (x, y) = (x as usize, y as usize); + if image[x][y] == oc { + q.push_back((x, y)); + image[x][y] = color; + } + } + } + } + + image + } +} diff --git a/solution/0700-0799/0733.Flood Fill/Solution2.ts b/solution/0700-0799/0733.Flood Fill/Solution2.ts new file mode 100644 index 0000000000000..344482e32d3cd --- /dev/null +++ b/solution/0700-0799/0733.Flood Fill/Solution2.ts @@ -0,0 +1,28 @@ +function floodFill(image: number[][], sr: number, sc: number, color: number): number[][] { + if (image[sr][sc] === color) { + return image; + } + + const oc = image[sr][sc]; + image[sr][sc] = color; + + const q: [number, number][] = []; + q.push([sr, sc]); + + const dirs = [-1, 0, 1, 0, -1]; + const [m, n] = [image.length, image[0].length]; + + while (q.length > 0) { + const [a, b] = q.shift()!; + for (let k = 0; k < 4; ++k) { + const x = a + dirs[k]; + const y = b + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] === oc) { + q.push([x, y]); + image[x][y] = color; + } + } + } + + return image; +}