Тёмный

Word Search II - Backtracking Trie - Leetcode 212 - Python 

NeetCode
Подписаться 748 тыс.
Просмотров 128 тыс.
50% 1

🚀 neetcode.io/ - A better way to prepare for Coding Interviews
🐦 Twitter: / neetcode1
🥷 Discord: / discord
🐮 Support the channel: / neetcode
Twitter: / neetcode1
Discord: / discord
⭐ BLIND-75 SPREADSHEET: docs.google.com/spreadsheets/...
💡 CODING SOLUTIONS: • Coding Interview Solut...
💡 DYNAMIC PROGRAMMING PLAYLIST: • House Robber - Leetco...
🌲 TREE PLAYLIST: • Invert Binary Tree - D...
💡 GRAPH PLAYLIST: • Course Schedule - Grap...
💡 BACKTRACKING PLAYLIST: • Word Search - Backtrac...
💡 LINKED LIST PLAYLIST: • Reverse Linked List - ...
Problem Link: neetcode.io/problems/search-f...
0:00 - Read the problem
5:06 - Drawing Explanation
12:32 - Coding Explanation
leetcode 212
This question was identified as a facebook interview question from here: github.com/xizhengszhang/Leet...
#trie #prefix #python
Disclosure: Some of the links above may be affiliate links, from which I may earn a small commission.

Наука

Опубликовано:

 

9 июл 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 210   
@NeetCode
@NeetCode 3 года назад
🚀 neetcode.io/ - I created a FREE site to make interview prep a lot easier, hope it helps! ❤
@amitupadhyay6511
@amitupadhyay6511 2 года назад
I faced this question in Uber interview. For fresh grad, they go with the hard problem in first round itself. Further, though I gave DFS solution they wanted trie one .
@sagarpotnis1215
@sagarpotnis1215 2 года назад
Damn this problem for fresh grads.. they expect too much
@symbol767
@symbol767 2 года назад
@@sagarpotnis1215 Yeah these interviewers got mental issues...
@markomekjavic
@markomekjavic Год назад
Which Uber office was that?
@SOMESHKHANDELIA
@SOMESHKHANDELIA 7 дней назад
The most enlightening moment for me was 4:40 : "Either you have heard of this data structure or you haven't". So true!
@boluo4790
@boluo4790 3 года назад
i always look for your leetcode videos 😄 Thanks for making these great videos!
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
Neet's solution as of June 2022 TLEs. To pass the Leetcode test cases you have to prune words from the prefix tree after you've found them. Here is the same as his solution with the addition of a `removeWord` method: ```python class TrieNode: def __init__(self): self.children = {} self.isWord = False def addWord(self, word): cur = self for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur = cur.children[c] cur.isWord = True def pruneWord(self, word) -> None: cur: TrieNode = self nodeAndChildKey: list[tuple[TrieNode, str]] = [] for char in word: nodeAndChildKey.append((cur, char)) cur = cur.children[char] for parentNode, childKey in reversed(nodeAndChildKey): targetNode = parentNode.children[childKey] if len(targetNode.children) == 0: del parentNode.children[childKey] else: return class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for w in words: root.addWord(w) ROWS, COLS = len(board), len(board[0]) res, visit = [], set() def dfs(r, c, node, word): if (r < 0 or c < 0 or r == ROWS or c == COLS or board[r][c] not in node.children or (r, c) in visit): return visit.add((r, c)) node = node.children[board[r][c]] word += board[r][c] if node.isWord: res.append(word) node.isWord = False root.pruneWord(word) dfs(r + 1, c, node, word) dfs(r - 1, c, node, word) dfs(r, c + 1, node, word) dfs(r, c - 1, node, word) visit.remove((r, c)) for r in range(ROWS): for c in range(COLS): dfs(r, c, root, "") return res ```
@NeetCode
@NeetCode 2 года назад
Thanks for the detailed comment!
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
@@NeetCode Thank you for all your hard work on these videos! You've done more for me learning algorithms than any of the books I've worked with. In particular, seeing how you visualize and work through problems allows me to internalize your mental representation of visually traversing the data structures.
@symbol767
@symbol767 2 года назад
So basically this algorithm got even heavier, damn
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
@@symbol767 Yeah, it's pretty gnarly. I've been practicing it the past few days but there's a lot going on while traversing both data structures contemporaneously. The prune method isn't so bad though. You only call it once in the main `dfs` helper.
@shubhammishra1225
@shubhammishra1225 2 года назад
@@PippyPappyPatterson What exactly prune word doing?
@ronsaxeditz7062
@ronsaxeditz7062 2 года назад
You're a life saver, these videos are amazing!
@NeetCode
@NeetCode 2 года назад
Happy to help!
@telnet8674
@telnet8674 2 года назад
final video on your playlist , thank you for this amazing content.
@MichaelShingo
@MichaelShingo Год назад
came back to this after doing Backtracking and Grid-graph problems in Blind 75 and it makes so much more sense
@tayjen59
@tayjen59 2 года назад
That's a good explanation, thank you man. However I have to notice something. On your site, the Trie topic comes before Backtracking and for people who solve problems in such an orderly way, it would be nice to have it sorted out a little.
@DragonStoneCreations
@DragonStoneCreations 2 года назад
Initially, I thought of building a Trie for the Maze and was sweating... but u just built a Trie on the words and made it like a piece of cake :D
@youreyesarebleeding1368
@youreyesarebleeding1368 26 дней назад
Man, i was SO close to getting the solution on this one, i spent maybe an hour on it. After I do all of the backtracking problems i'll return to this one and try to solve it on my own again, I was thinking about this one for hours. great video, thanks for all of the resources you've put out there!
@rohanramani6243
@rohanramani6243 2 года назад
How tf am I supposed to solve this in a 35 min interview lol
@bradleyhampton7966
@bradleyhampton7966 6 месяцев назад
I just got this problem as part of a 70 minute online assessment with 4 total problems 💀
@buttofthejoke
@buttofthejoke 6 месяцев назад
​@@bradleyhampton7966which company?
@crazeeealgorithms3236
@crazeeealgorithms3236 5 месяцев назад
Great!!
@sidhartheleswarapu
@sidhartheleswarapu 3 месяца назад
@@bradleyhampton7966 what company?
@akialter
@akialter 3 месяца назад
I think it’s supposed to be follow up of word search I, and probably only given verbal solution and only code if time is permitted
@cansuyanik
@cansuyanik 2 года назад
19:54 instead of doing this, node.isWord can be set to false after adding word to res in the dfs function.
@kockarthur7976
@kockarthur7976 Год назад
This doesn't really optimize the algorithm. It will only prevent the algorithm from adding the same word to 'res' twice, but all the nodes will still be present in the Trie. For example, suppose our only word in our word-list is 'oath', and that we have a huge board which has many 'o' and 'a' letters. Suppose we find our word 'oath' starting at the first cell. Ideally we would fully remove 'oath' from our remaining words, and therefore immediately exit the algorithm. If we only set node.isWord to false but leave the Trie structure intact, then we will go through the entire board and find 'o' many times, performing many long recursive DFS calls. This is why we should 'prune' the Trie, i.e. completely remove each found word from our Trie.
@ZGuo-hf6xb
@ZGuo-hf6xb Год назад
​@@kockarthur7976 Sharp points!
@TrueVasember
@TrueVasember 6 месяцев назад
i did the same
@SOMESHKHANDELIA
@SOMESHKHANDELIA 7 дней назад
@@kockarthur7976 You cannot "prune" the Trie. Because the currently found complete word may be a prefix of another word. Example: you are looking for "oa" and "oaa". If you prune the "Trie" after finding "oa", you may not find "oaa" because probably 'oaa' only existed as overlapping with "oa"
@infiniteloop5449
@infiniteloop5449 2 года назад
Does it make sense to put the add word method in the TrieNode class since it is only called from the root node vs putting it in the solution class. This should have a lower memory footprint as addWord method is instantiated every time a TrieNode is created rather than once when addWord is called to create the prefix tree.
@linhdinh136
@linhdinh136 3 года назад
Awesome explanation! Thank you ...
@NeetCode
@NeetCode 3 года назад
Thanks 😊
@hazelnut5458
@hazelnut5458 2 года назад
love your videos so much! Please upload more!! So helpful.
@monadyczny
@monadyczny 2 месяца назад
It was for me a last problem from NeetCode 150 list Thanks a lot for that journey. It was sometimes frustrating, but rewarding in the end.
@GopalKumar-lx7hn
@GopalKumar-lx7hn Год назад
We can get TLE, better to mark node.isWord = false while adding the word in res. Bonus, by this we can use list directly instead of set, as we guaranty, all the words occur only once.
@shashikiran5385
@shashikiran5385 15 дней назад
The actual Optimization is that, we need to keep track of how many words is prefixed from a particular Node in the Trie. This is done during the insertion of words. Now whenever we find a word during the search, we should recursively decrement the count of that node and all the nodes directly above it till root. When this count reaches 0 at any particular node, we should prune that node branch. This would be a lot more optimized. But yes, the time complexity would not decrease. It would only be efficient in this particular problem.
@saurabhruikar1131
@saurabhruikar1131 Год назад
we can also add a another base case if len(res)>=len(words): return as there is no point in further iteration after finding all the words.
@linli7049
@linli7049 3 года назад
Good explanation as always
@overcharged2078
@overcharged2078 Год назад
in your site this comes before backtracking, is hard to follow up or solve without backtracking knowledge, good explanation though :)
@Tensor08
@Tensor08 Год назад
Beautiful !
@ivandrofly
@ivandrofly 3 месяца назад
Thanks you made it very simple
@kockarthur7976
@kockarthur7976 Год назад
I was still getting TLE even after implementing the pruning. I found an optimization that finally made my solution go through, with very fast runtime. The idea is to remove all words that could not possibly be constructed by our board via letter-adjacencies. At the beginning of the findWords function, make a dictionary of sets called `adjacencies`. Each element of this dictionary represents all the letters which are adjacent to a given letter, i.e. adjacencies[ 'a' ] = { ' b' , 'c' } means that the only letters which border an 'a' are 'b' and 'c'. Then make a function which checks if a given word can be constructed from the board. Go through each character in the word (excluding the last one), and check if the character in front of it can be reached from it, i.e. if w[i+1] is in adjacencies[w[i]]. Run this function on every word in the list. If a word cannot be created by our board, we simply don't include it.
@ga_per
@ga_per 7 месяцев назад
great solution
@user-le6ts6ci7h
@user-le6ts6ci7h 7 месяцев назад
But this will not work, as there can be multiple occurence of same character but each with different set of neighbours
@ga_per
@ga_per 7 месяцев назад
@@user-le6ts6ci7h Your statement doesn't make sense. The strategy is to use a hashmap to store all possible neighbours for each character by iterating once over the board. Note that it stores the neighbours for each CHARACTER, instead of the neighbours for each index. Thus, the algorithm does consider different sets of neighbours for different occurrences of the same character.
@ga_per
@ga_per 7 месяцев назад
@@user-le6ts6ci7h Also note that this is just to check if the word can be constructed considering the board display and the letter adjacencies. We still run the main algorithm to validate whether the word can be constructed or not.
@user-le6ts6ci7h
@user-le6ts6ci7h 7 месяцев назад
@@ga_per Yeah if this is just to remove the words that can't be formed , yeah it helps to cut down the TC
@WhisperingWalnuts
@WhisperingWalnuts 2 года назад
this solution is timing out now since the best solution in leetcode have done a stronger tweaks. Thats dumb on leetcode! But, thanks a lot for this video!
@leetcoderlandon8353
@leetcoderlandon8353 2 года назад
optimization: 1. whenever you add a word, you can set the isword to None, then can set res as a list, shouldn't have duplicates 2. visit is not needed, set the board value to None before going to the next depth
@NeetCode
@NeetCode 2 года назад
definitely good points
@echovave
@echovave 2 года назад
Thanks, the code was failing at one test case, this helped. Now it passed all the test cases.
@sorayamorshedizadeh5965
@sorayamorshedizadeh5965 2 года назад
@@echovave @Neetcode, I see these optimization will work, but I do not understand why they make the code faster. For item1, is list faster than set? For item 2. I think adding a word to set and check if the word in that set is O(1), so how this changes make the code faster?
@tarek7451
@tarek7451 2 года назад
Doing those two did help me to pass the test case sometimes. However, it still results in TLE often times. Any more optimization that could be done?
@ashuzon
@ashuzon 6 месяцев назад
God bless you! Backtracking, dfs and even trie. This is way high profile.
@joshpark818
@joshpark818 2 года назад
3:33 isn't the time complexity for the naive solution wmn4^s where s is the longest word in the words array? Since at max we're going to traverse s amount of letters in 4 directions
@rohitsunil5143
@rohitsunil5143 2 года назад
Yup this is right
@tayjen59
@tayjen59 2 года назад
nope, at most mxn, even if there is a word longer than mxn (for example m = 1, n = 1, and words = ['apple'])
@manoelquirino5081
@manoelquirino5081 3 года назад
Nice explanation, I implemented using a trie in the board, but I came to your video to see what I was doing wrong hehehe. In ruby I needed to remove the word from the trie when found and the runtime was 208ms. I needed to track the parent node in each node so I can reverse remove.
@lxu5148
@lxu5148 Год назад
Could you kindly explain why we need to remove the word?
@manoelquirino5081
@manoelquirino5081 Год назад
@@lxu5148 I don't remember at all 🤣
@lxu5148
@lxu5148 Год назад
@@manoelquirino5081 It's okay. It's one-year-ago.😆
@habalgarmin
@habalgarmin Год назад
Your solution really helped me to fix TLE. Shame on me, I couldn't come up with the right solution on my own =(
@leok2388
@leok2388 2 года назад
A way to solve the TLE is to add a prune method to the TrieNode class. The idea is when you find a word you delete it from the tree in order to optimize the time complexity. def prune(self, word): curr = self stack = [] for ch in word: stack.append(curr) curr = curr.children[ch] curr.is_word = False for t_node, ch in reversed(list(zip(stack, word))): if len(t_node.children[ch].children) > 0: # has children return else: del t_node.children[ch]
@thebotbob6317
@thebotbob6317 2 года назад
brilliant idea! I get it.
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
bump for others
@jaejincho9921
@jaejincho9921 2 года назад
Thanks for sharing this. There is another way to implement this idea in code by adding self.parent attribute to TrieNode class to point to the parent node, and pruning easily.
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
@@jaejincho9921 Ohhhhhh, that's awesome. Is there a way to reference the calling scope's object from the `__init__` constructor? Or would you need to initialize `trie_node1.parent = cur` each time you construct a new TrieNode?
@kattathoyajkirankiran9896
@kattathoyajkirankiran9896 2 года назад
What is the time complexity after pruning?
@rishmithahahaha
@rishmithahahaha 2 месяца назад
If anybody is wondering why word and node are not reversed/popped just like visited set, it is because their states are maintained in the stack frames itself. we do it explicitly for sets, because its a reference and the value keeps changing throughout the calls and the states are not maintained. But for node and string, when you return back after a recursive call, state will be maintained as soon as you enter that local function frame.
@ameynaik2743
@ameynaik2743 2 года назад
you could technically prune the part of the tree once a word is found?
@abhinavsinghkushwah2416
@abhinavsinghkushwah2416 3 года назад
Thanks!!
@praneenderguptavuppala3398
@praneenderguptavuppala3398 2 года назад
Instead of using a trie DS, Can we use hash set to store the input words and run the dfs algorithm to check if the word exists in the hashset in every recursive call (searching for a word in hashset time complexity is O(1))? Please let me know the cons of using this approach.
@dinhnhobao
@dinhnhobao 2 года назад
It works but I reckon it would be very slow. You basically need to generate all the words possible from the board, which corresponds to all the possible paths between any two cells in the board (since each path between any two cells correspond to a word). If N is the number of cells then that would be at least O(N^2) (or O(N^3), I'm not too sure). Imagine if you have a very huge board (10^9 x 10^9), but only a few words (1 or 2 short words). It's obvious that by building the trie, the algorithm will be faster since you are mostly traversing on the trie (while checking if the trie traversal is possible based on the board). For your algorithm, it will traverse on the board and generate billions of unnecessary words to check if the word exists in the HashSet.
@s.prakash5784
@s.prakash5784 2 года назад
Tried this same code...im getting time limit exceeeded....what should i do?
@ericwu1002
@ericwu1002 2 года назад
Thanks! Just a note that a straight translation to CPP won't work. I got TLE out of this. You need to get rid of visited set and the result set and do what " Leet coder Landon" said in the comment
@gabchen3000
@gabchen3000 10 месяцев назад
This was my approach for removing the word from Trie to overcome Time Limit Exceeded on python: [... in Trie class ...]: def removeWord(self, word): cur = self for c in word: if len(cur.child[c].child) == 0: del cur.child[c] return else: cur = cur.child[c] [... in dfs function ...]: if node.end: res.add(word) # this is optimization, remove the word after it has been found node.end = False root.removeWord(word) hope this helps!
@kritmok1875
@kritmok1875 9 месяцев назад
This is the easiest to understand for word removal.. Thank you.
@jonathannwokeji2639
@jonathannwokeji2639 День назад
Wait wouldn't this just remove the leaf node and not the entire word?
@rajivroy1175
@rajivroy1175 2 года назад
excellent video.
@zhaovincent8039
@zhaovincent8039 2 года назад
Hi Sir! Could you explain the time and space complexity of this? I use exact code like you wrote, now sometimes on leetcode it will get time limit exceed, do you have any solution for optimizing the time complexity? Thank you!
@bhavikpatelid
@bhavikpatelid 2 года назад
It seems leetcode added few more testcases since this video was created. New testcases are wrote around many combinations of given word in the matrix which causes TLE. Implement removing the word from trie once added to the result. This would avoid TLE.
@cpaulicka
@cpaulicka 2 года назад
@@bhavikpatelid Thanks so much for this...and fortunately, the lazy approach of just popping the node if it has no children was enough. I was worried I would have to write a full remove() method.
@shreekarshastry2307
@shreekarshastry2307 Год назад
@@bhavikpatelid for ex: words [apple,app, ace] once apple is done, remove apple or e only?
@rongrongmiao3018
@rongrongmiao3018 2 года назад
help... 2022 dfs with trie is timing out on leetcode
@sagarpotnis1215
@sagarpotnis1215 2 года назад
Whats the time /space complexity for this Problem ?
@Adnarimel
@Adnarimel 2 года назад
Did he mention the time complexity of his solution like he did for the brute force solution at the beginning? I didn't see it
@radishanim
@radishanim 2 года назад
At 3:35, he says that we go from O(wmn*4^mn) (brute force time complexity) to O(mn*4^mn). However, I think he may have been incorrect about the time complexity because the actual brute force soln complexity should be O(wmn*4^s) where s= len(longestString) (unless he meant s= length of total characters in a word which in the worst case, would be of len m*n). The solution he proposes here searches through all of the characters, so I think the runtime is O(mn*4^s)
@edwardteach2
@edwardteach2 2 года назад
U a God
@WealthyYoungstersClub
@WealthyYoungstersClub Год назад
this need and improvement facts. this code will give you Time limit exceded
@ayush51379
@ayush51379 2 года назад
Getting Time Limit Exceeded error. It is my first time facing error in your solution. Code runs for my own test cases, but for overall encounters TLE. Still your video is very helpful to understand this question and approaches. Leetcode solution has additional step of removing nodes from Trie if it was leaf and part of a word, to keep reducing size of Trie, maybe that's the reason of TLE. Your channel is awesome, man. I have been learning so much from your videos in a simple and easy manner. Have a good day!
@shraddhagami7910
@shraddhagami7910 2 года назад
Also getting TLE to me
@cici-lx6np
@cici-lx6np 2 года назад
@@shraddhagami7910 class TrieNode(): def __init__(self): self.children = {} self.EoW = False def addWord(self, word): curr = self for c in word: if c not in curr.children: curr.children[c] = TrieNode() curr = curr.children[c] curr.EoW = True class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for w in words: root.addWord(w) ROWS, COLS = len(board), len(board[0]) res = set() def dfs(r, c, node, word): if (r
@shraddhagami7910
@shraddhagami7910 2 года назад
@@cici-lx6np it is also showing TLE
@cici-lx6np
@cici-lx6np 2 года назад
@@shraddhagami7910 Thanks for letting me know. I retested, it failed :( Maybe we need to try other methods
@adeniyiadeboye3300
@adeniyiadeboye3300 2 года назад
try to modify the TrieNode class (it will work, even though the submission is still not that fast): class TrieNode: def __init__(self): self.children = defaultdict(TrieNode) self.isWord = False def addWord(self, word): cur = self for c in word: cur = cur.children[c] cur.isWord = True
@orellavie6233
@orellavie6233 2 года назад
The complexity sounds like O(n*m*x*y), where n is num of words, m is the max num of each word, and we do 4 dfs with visitset on the matrix, so we again search all matrix (size x*y). Couldn't we save for each subword a hashmap of the least node (word), so we could search from it further?
@VadimFilin
@VadimFilin Год назад
Nice solution with noodle code
@carolli7604
@carolli7604 Год назад
I implemented the pruning by adding a parent variable and val variable to TrieNode: class TrieNode: def __init__(self): self.children = {} self.parent = -1 self.isWord = False self.val = '' def addWord(self, word): cur = self for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur.children[c].parent = cur cur.children[c].val = c cur = cur.children[c] cur.isWord = True def pruneWord(self): cur = self while len(cur.children) == 0 and cur.parent != -1: par = cur.parent del par.children[cur.val] cur = par class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for w in words: root.addWord(w) ROWS, COLS = len(board), len(board[0]) res, visit = [], set() def dfs(r, c, node, word): if r < 0 or c < 0 or r == ROWS or c == COLS or (r,c) in visit or board[r][c] not in node.children: return visit.add((r,c)) node = node.children[board[r][c]] word += board[r][c] if node.isWord: res.append(word) node.isWord = False node.pruneWord() dfs(r + 1, c, node, word) dfs(r - 1, c, node, word) dfs(r, c + 1, node, word) dfs(r, c - 1, node, word) visit.remove((r,c)) for i in range(ROWS): for j in range(COLS): dfs(i, j, root, "") return res
@veliea5160
@veliea5160 Год назад
funny moment at 4.40. "Either you have heard of this data structure before or you have not". No way :)
@kaushaldawra3527
@kaushaldawra3527 3 года назад
Great explanation as always. Could you please please do one soon on the Egg Dropping and Cherry Pickup?
@NeetCode
@NeetCode 3 года назад
Sure I've been wanting to do egg drop for a while. To be honest I still get nightmares from Cherry pickup 😨🍒🍒🍒
@kaushaldawra3527
@kaushaldawra3527 3 года назад
@@NeetCode +1 Life hasn't been the same since cherry pickup
@boluo4790
@boluo4790 3 года назад
@@NeetCode egg drop!! I feel hard to understand that problem
@arnabpersonal6729
@arnabpersonal6729 2 года назад
@@boluo4790 take best of the worst cases for egg drop
@videowatcher4852
@videowatcher4852 11 месяцев назад
I have a question regarding the backtracking/recursion. Do we not need to pass in current vistited set as parameters to the function? why or why not??
@atomicgray
@atomicgray 11 месяцев назад
the dfs function has access to the scope of outer function. its a closure
@Ben-pb7ct
@Ben-pb7ct Год назад
I wonder what does “ref” mean in Java solution. I found the variable “ref” defined in Trie class.
@richinoya_
@richinoya_ 2 года назад
Thanks for the great solution and explanation, but I'm getting TLE with this code as of April 2022
@dollyvishwakarma2
@dollyvishwakarma2 2 года назад
I found this in the discussion section of the question: leetcode.com/problems/word-search-ii/discuss/1860991/python3-trie%2Bdfs%2Bpruning This solution is a modification which works.
@richinoya_
@richinoya_ 2 года назад
@@dollyvishwakarma2 yep that is the way to do it. I think it's really stupid that the normal backtracking dfs solution can't even be accepted without such an advanced pruning implementation... what's the point of the execution time rankings if only the most optimal solution can be accepted?
@SOMESHKHANDELIA
@SOMESHKHANDELIA 7 дней назад
This same solution in C++ is giving me TLE. I even changed the visited set to an unordered_map and still cannot clear the TLE.
@symbol767
@symbol767 2 года назад
This is a heavy algorithm, like very complex, damn
@NeetCode
@NeetCode 2 года назад
Agree
@strawberriesandcream2863
@strawberriesandcream2863 7 месяцев назад
Here is a simple variation of this solution that doesn't get TLE on LC. Don't start the DFS unless it could lead to a solution, and maintain the word at the last character's Trie Node so we don't need to maintain a local word list. Also, once we find a word we can reset TrieNode.word to "" so that word won't be looked for again. class TrieNode: def __init__(self): self.children = {} self.word = "" def addWord(self, word): root = self for c in word: if c not in root.children: root.children[c] = TrieNode() root = root.children[c] root.word = word class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for word in words: root.addWord(word) ROWS, COLS = len(board), len(board[0]) res, visited = set(), set() def dfs(r, c, node): if r < 0 or r >= ROWS or c < 0 or c >= COLS or (r, c) in visited or board[r][c] not in node.children: return if node.children[board[r][c]].word: res.add(node.children[board[r][c]].word) node.children[board[r][c]].word = "" visited.add((r, c)) node = node.children[board[r][c]] dfs(r + 1, c, node) dfs(r - 1, c, node) dfs(r, c + 1, node) dfs(r, c - 1, node) visited.remove((r, c)) for r in range(ROWS): for c in range(COLS): if board[r][c] in root.children: dfs(r, c, root) return list(res)
@strawberriesandcream2863
@strawberriesandcream2863 7 месяцев назад
and with this solution, res would not need to be a set since we would never look for the same word twice.
@tianyulu5673
@tianyulu5673 7 месяцев назад
Great!
@mangalegends
@mangalegends 2 года назад
Can't seem to get it to pass the last test case :/
@il5083
@il5083 Год назад
The time limit on this problem is so trash I came up with a solution similar to the video, and I have to do almost every possible optimization for over an hour to make it get accepted.
@ashokchowdary3366
@ashokchowdary3366 11 месяцев назад
How about converting the matrix to Trie and searching for words?
@TechOnScreen
@TechOnScreen 2 года назад
it says Time Limit Exceeded for similar java solution!
@gnes04
@gnes04 5 месяцев назад
I did this in C++ and even after adding a remove function to the Trie class and getting rid of words i've already found, It's going over time limit.
@s8x.
@s8x. 10 месяцев назад
it won't pass the test case for "oa" and "oaa"
@shalsteven
@shalsteven 8 месяцев назад
use set() for storing the answer
@shai1esh
@shai1esh Год назад
One potential optimization you can make is to use a prefix tree (Trie) to store the words instead of checking for each word individually.
@TheRacingmanic
@TheRacingmanic 2 года назад
Great video, but there's now some failing test cases 44/62
@PippyPappyPatterson
@PippyPappyPatterson 2 года назад
Yeah this solution TLEs now.
@Yash-uk8ib
@Yash-uk8ib 2 года назад
is it 4^(length of total characters in WORDS array which in worst case might grow to m*n)?? This is what you meant??
@mohammedfahadnyc1385
@mohammedfahadnyc1385 Год назад
The Worst Case would be O(mn * 4* 3^ (length of words -1)) ...
@TheLaidia
@TheLaidia Месяц назад
why there could be duplicates?
@amirilifee
@amirilifee 11 месяцев назад
How u come up with all of this solution ideas bro? it took me like 20 minutes to realise why your code works 🤣
@arnabpersonal6729
@arnabpersonal6729 2 года назад
This is quite hard problem
@NeetCode
@NeetCode 2 года назад
Agreed!
@shreshtashetty2985
@shreshtashetty2985 2 года назад
Is it just me, or does anyone else also get a Time Limit Exceeded error for this solution? Any possible fixes so it can run within the given time limit?
@WoWkiddymage
@WoWkiddymage 2 года назад
yeah I was super confused, my approach was slightly different data structures but similar runtimes. I even tried changing to his data types and I still get hecka runtime exceded.
@pd.dataframe2833
@pd.dataframe2833 2 года назад
you need to implement tree pruning too. basically remove words from trie if they are already added to result. this way u wont be doing dfs for those prefixes
@WoWkiddymage
@WoWkiddymage 2 года назад
@@pd.dataframe2833 yeah you are 100% correct pruning was what made it the solution submit, but you have to do this from the dfs (not just removing it from the root). Thanks for the response!
@user-gk7id3jv7q
@user-gk7id3jv7q 2 года назад
@@WoWkiddymage can you provide this solution?
@WoWkiddymage
@WoWkiddymage 2 года назад
@@user-gk7id3jv7q it's similar to this solution, but used a stack (similar to the visited set) to keep track of the nodes I came from. So when you find a word, I would remove the "end" tag from the trie node for the end of that word. Then if it's a leaf node prune using the stack path
@EverythingTechWithMustafa
@EverythingTechWithMustafa 2 года назад
Neet explanation
@darkness35869
@darkness35869 Год назад
I solved this problem by myself, but it's slow to pass the test cases in leetcode
@harishsn4866
@harishsn4866 2 года назад
This gives time limit exceeded error.
@narsimhareddy7224
@narsimhareddy7224 Месяц назад
i did the trie for the matrix instead of given words lol
@raunakthakur7004
@raunakthakur7004 3 года назад
What's the complexity in this problem?
@webtalks6760
@webtalks6760 Год назад
?
@haoyuwang3243
@haoyuwang3243 Год назад
How to prune based on your code? This gets TLE now.
@morenoh149
@morenoh149 11 месяцев назад
you should redo this one
@bhargavimopuru8576
@bhargavimopuru8576 2 года назад
inputs are after 4 a b c d f e f g t e r r a b i j output wants to come as [(0,0),(1,0),(2,0),(2,1),(2,2)] different inputs are search 4 s e a c r c c c h e e e h e e e output wants to come as "no such word is there" how will do this in python pls explain
@rajrsa
@rajrsa Год назад
Got a TLE? Neet has already posted a solution for it on the site: class TrieNode: def __init__(self): self.children = {} self.isWord = False self.refs = 0 def addWord(self, word): cur = self cur.refs += 1 for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur = cur.children[c] cur.refs += 1 cur.isWord = True def removeWord(self, word): cur = self cur.refs -= 1 for c in word: if c in cur.children: cur = cur.children[c] cur.refs -= 1 class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for w in words: root.addWord(w) ROWS, COLS = len(board), len(board[0]) res, visit = set(), set() def dfs(r, c, node, word): if ( r not in range(ROWS) or c not in range(COLS) or board[r][c] not in node.children or node.children[board[r][c]].refs < 1 or (r, c) in visit ): return visit.add((r, c)) node = node.children[board[r][c]] word += board[r][c] if node.isWord: node.isWord = False res.add(word) root.removeWord(word) dfs(r + 1, c, node, word) dfs(r - 1, c, node, word) dfs(r, c + 1, node, word) dfs(r, c - 1, node, word) visit.remove((r, c)) for r in range(ROWS): for c in range(COLS): dfs(r, c, root, "") return list(res) @NeetCode, it would be great if you could explain this a bit more. :-)
@danielsun716
@danielsun716 Год назад
14:41, in line 7, Why cur should be "self" not "TrieNode()"? In line 10, why cur.children[c] should be "TrieNode()"? not "self"? I am so confused about that.
@Mustafa-099
@Mustafa-099 Год назад
In line 10, why cur.children[c] should be "TrieNode()"? not "self"? Because we want to create a Trienode at the key "c" because the if clause above only let's this line to execute IF the "c" does not exist already in the dictionary
@Mustafa-099
@Mustafa-099 Год назад
in line 7, Why cur should be "self" not "TrieNode()"? Because we want to have the cur to point to the node that is being processed at the moment Remember this, self refers to the current instance of the class that was made, so having cur to point at self ensures that we are not starting all over from a root node everytime this method of addword() is called
@Mustafa-099
@Mustafa-099 Год назад
Bro I would recommend going over OOP concepts again and everything will be cleared once again for you, I had to do the same a while ago and it's been good to get a refresher on those topics
@xBazilio
@xBazilio 2 года назад
leetcode is finicky and won't accept the same solution written in PHP: Time Limit Exceeded.
@chenzhuo9
@chenzhuo9 3 года назад
horray
@lingyuhu4623
@lingyuhu4623 Год назад
I tried many ways, still can't pass all the test😭
@lingyuhu4623
@lingyuhu4623 Год назад
add node.isWord = False below line res.append(word)
@jonaskhanwald566
@jonaskhanwald566 3 года назад
Bouncer
@tonyz2203
@tonyz2203 2 года назад
this solution will be TLE in 2022
@NeetCode
@NeetCode 2 года назад
Lol
@user-bj5pn1tf7o
@user-bj5pn1tf7o 2 года назад
same issue :(
@bhaveshsuhagia
@bhaveshsuhagia 2 года назад
what's the optimized solution?
@krishnasharma657
@krishnasharma657 7 дней назад
It doesn't in 2024😂
@Albert-lr7ky
@Albert-lr7ky Год назад
apec not found? try apex
@alright8255
@alright8255 2 года назад
If you are getting Time Limit Exceeded, try this. (As of 2022 July) if curr.isWord: res.append(word) curr.isWord=False if not curr.children: node.children.pop(board[r][c],None) (I also used a list instead of a set for res, so you can just return res instead of list(res). Setting curr.isWord = False takes care of duplicates)
@sochintzy4528
@sochintzy4528 Год назад
Edit: I figured it out. I had to store the original node in a temp variable before line 31 in the video where node is updated. Before Line 31: temp = node After Line 34: node.isWord = False if not node.children: temp.children.pop(board[r][c], None) Hello, I am very confused as to why you are using "curr" in your if statements but you used "node" for the last line, can you please explain. It is timing out for me & I'm not sure if your block of code is in the Trie class or in the dfs() due to these conflicting variables.
@AnonymousCapybara-xf1mj
@AnonymousCapybara-xf1mj Год назад
@@sochintzy4528 Thank you for clarifying here and even specifying how to edit the code! 7 months later and I was looking for a simple change I could make rather than the complicated edits others were suggesting. This is the most underrated solution here☺
@emilybao
@emilybao 2 года назад
Did anyone else get an index out of range error for the if statement for the dfs basecase?
@amol_
@amol_ 5 месяцев назад
For Passing last testcase, we need to prune the word if children of that word is zero after the dfs calls. example: if(child.isCompleteWord) { presentWords.add(word); } dfs(i + 1, j, board, child, word, visited, presentWords); dfs(i - 1, j, board, child, word, visited, presentWords); dfs(i, j + 1, board, child, word, visited, presentWords); dfs(i, j - 1, board, child, word, visited, presentWords); // prune logic after exploring all paths from i and j if(child.isCompleteWord) { child.isCompleteWord = false; if(child.children.size() == 0) { trie.children.remove(currentChar); } } complete code: class Node { HashMap children; boolean isCompleteWord; public Node() { children = new HashMap(); isCompleteWord = false; } public void insert(String word) { if(word.isEmpty()) { isCompleteWord = true; return; } char startChar = word.charAt(0); String remaining = word.substring(1); if(children.containsKey(startChar)) { children.get(startChar).insert(remaining); } else { Node child = new Node(); children.put(startChar, child); child.insert(remaining); } } public String toString() { return "{ children: " + children + " , isCompleteWord: " + isCompleteWord + " }"; } } class Solution { public List findWords(char[][] board, String[] words) { Node trie = buildTrie(words); // System.out.println(trie.toString()); List presentWords = new ArrayList(); HashSet visited = new HashSet(); for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { dfs(i, j, board, trie, "", visited, presentWords); } } return presentWords; } private void dfs(int i, int j, char[][] board, Node trie, String word, HashSet visited, List presentWords) { String key = i + "|" + j; if(visited.contains(key)) { return ; } int row = board.length; int col = board[0].length; if(i >= row || i < 0 || j >= col || j < 0) { return; } char currentChar = board[i][j]; if(trie.children.containsKey(currentChar)) { visited.add(key); Node child = trie.children.get(currentChar); word = word + "" + currentChar; if(child.isCompleteWord) { presentWords.add(word); } dfs(i + 1, j, board, child, word, visited, presentWords); dfs(i - 1, j, board, child, word, visited, presentWords); dfs(i, j + 1, board, child, word, visited, presentWords); dfs(i, j - 1, board, child, word, visited, presentWords); if(child.isCompleteWord) { child.isCompleteWord = false; if(child.children.size() == 0) { trie.children.remove(currentChar); } } visited.remove(key); } } private Node buildTrie(String[] words) { Node trie = new Node(); for(String word: words) { trie.insert(word); } return trie; } }
@uniquename2386
@uniquename2386 Год назад
For people who got TLE: You could try instead of storing visit set, mark path with '#' class TrieNode: def __init__(self): self.children = {} self.endOfWord = False def addWord(self, word): cur = self for c in word: if c not in cur.children: cur.children[c] = TrieNode() cur = cur.children[c] cur.endOfWord = True class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: root = TrieNode() for w in words: root.addWord(w) rows, cols = len(board), len(board[0]) res = set() def dfs(r, c, node, word): if r < 0 or c < 0 or r == rows or c == cols or board[r][c] == '#' or board[r][c] not in node.children: return False node = node.children[board[r][c]] word += board[r][c] tmp = board[r][c] board[r][c] = '#' if node.endOfWord: res.add(word) dfs(r + 1, c, node, word) dfs(r - 1, c, node, word) dfs(r, c + 1, node, word) dfs(r, c - 1, node, word) board[r][c] = tmp for r in range(rows): for c in range(cols): dfs(r, c, root, '') return list(res)
@Mustafa-099
@Mustafa-099 Год назад
Bro this is soooo genius!! Thank you for sharing the solution You are awesome! 🙂
@MohamedIbrahim-yg7of
@MohamedIbrahim-yg7of Год назад
still TLE
@Mustafa-099
@Mustafa-099 Год назад
@@MohamedIbrahim-yg7of works for me bro, you can check the code once again perhaps?
@MohamedIbrahim-yg7of
@MohamedIbrahim-yg7of Год назад
​@@Mustafa-099 thanks , it's work can we practice together?
@Mustafa-099
@Mustafa-099 Год назад
@@MohamedIbrahim-yg7of Sure man why not :)
@dhanrajbhosale9313
@dhanrajbhosale9313 Год назад
Just small modification to avoid TLE ####################### 1. Fasle the EndOfWord 2. Adding num_of_word variable class TrieNode: def __init__(self): self.children = {} self.EndOfWord = False def add_word(self, word): cur = self for c in word: if c not in cur.children: cur.children[c]=TrieNode() cur = cur.children[c] cur.EndOfWord = True class Solution: def __init__(self): self.num_of_words = 0 def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: self.num_of_words = len(words) root = TrieNode() for word in words: root.add_word(word) res, visit = [], set() def dfs(x, y, word, node): if x=len(board[0]) or board[x][y] not in node.children or (x,y) in visit or self.num_of_words == 0: return visit.add((x,y)) node = node.children[board[x][y]] word+=board[x][y] if node.EndOfWord: res.append(word) node.EndOfWord = False #################################### self.num_of_words-=1 ############################### dfs(x+1, y, word, node) dfs(x, y+1, word, node) dfs(x-1, y, word, node) dfs(x, y-1, word, node) visit.remove((x,y)) for i in range(len(board)): for j in range(len(board[0])): dfs(i, j, "", root) return res
@jerrybao1934
@jerrybao1934 11 месяцев назад
Thank you for your update! For those who might not understand why we mark node.EndOfWord as False in that if statement marked with #####, by marking the ending character now as False, this word in the Trie will no longer be checked. Even if starting from another cell we get to this word, the algorithm will carry on because it no longer recognizes this as the word. This avoids TLE issue because we won't be checking for the same word for multiple times. (i.e, No need to check again if a word can be found, if the word is already found from other cells previously)
@dhanrajbhosale9313
@dhanrajbhosale9313 11 месяцев назад
@@jerrybao1934 well explained. Thanks.
@geekydanish5990
@geekydanish5990 Год назад
class TrieNode: def __init__(self): self.children = {} self.is_end = False class Trie: def __init__(self,root_node): self.root = root_node def insert_word_into_trie(self,word): curr_node = self.root for char in word: if char not in curr_node.children: curr_node.children[char] = TrieNode() curr_node = curr_node.children[char] curr_node.is_end = True def prune_word(self, word): curr_node = self.root stack = [] for char in word: stack.append(curr_node) curr_node = curr_node.children[char] curr_node.is_word = False for trie_node, char in reversed(list(zip(stack, word))): if len(trie_node.children[char].children) > 0: # has children return else: del trie_node.children[char] class Solution: def findWords(self, grid: List[List[str]], words: List[str]) -> List[str]: root_node = TrieNode() trie = Trie(root_node) for word in words: trie.insert_word_into_trie(word) ROWS,COLS = len(grid), len(grid[0]) visit = set() res = [] directions = [(0,1),(0,-1),(-1,0),(1,0)] def dfs(r,c,pref_word,curr_node): if r < 0 or c < 0 or r == ROWS or c == COLS or grid[r][c] not in curr_node.children or (r,c) in visit: return visit.add((r,c)) curr_node = curr_node.children[grid[r][c]] pref_word += grid[r][c] if curr_node.is_end: res.append(pref_word) trie.prune_word(pref_word) for dr, dc in directions: dfs(r+dr,c+dc,pref_word,curr_node) visit.remove((r,c)) for r in range(ROWS): for c in range(COLS): dfs(r,c,"",root_node) return list(set(res))
@mohamadilhamramadhan6354
@mohamadilhamramadhan6354 Год назад
function dfs() { .... (r < 0 || c < 0) || (r === ROWS || c === COLS) || ( visit.has(`${r},${c}`)) || (! (board[r][c] in node.children) ) || (res.size === words.length) //
@pacomarmolejo3492
@pacomarmolejo3492 Год назад
it does help, kept getting TLE. Thanks man.
Далее
N-Queens - Backtracking - Leetcode 51 - Python
17:51
Просмотров 152 тыс.
How I would learn Leetcode if I could start over
18:03
Просмотров 315 тыс.
Software Engineering Job Interview - Full Mock Interview
1:14:29
Implement Trie (Prefix Tree) - Leetcode 208
18:56
Просмотров 179 тыс.
How to Solve ANY LeetCode Problem (Step-by-Step)
12:37
Просмотров 127 тыс.
5 Useful F-String Tricks In Python
10:02
Просмотров 273 тыс.
8 patterns to solve 80% Leetcode problems
7:30
Просмотров 232 тыс.
Word Break II - Leetcode 140 - Python
20:39
Просмотров 10 тыс.
Подключил AirPods к Xbox
0:45
Просмотров 26 тыс.
Acer Predator Тараканьи Бега!
1:00
Просмотров 337 тыс.
Красиво, но телефон жаль
0:32
Просмотров 122 тыс.