A good way into this one is to ask: what do we need to remember so we never redo work blindly? For 720. Longest Word in Dictionary, the solution in this repository is mainly a trie solution.
Guide
What?
The code is easier to read if we treat it as a controlled search through possible states. Instead of trying to be clever immediately, read the code as a sequence of questions:
- What state are we keeping?
- How do we move from one state to the next?
- When do we know the answer is already determined?
For this file, the main tools are: trie, two pointers, stack, prefix sums.
Guide
When?
Use this approach when the hard part is not syntax, but deciding what must stay true after every update. The accepted code reduces that pressure by storing exactly the information that remains useful later.
The important function names to track are longestWord, insert, dfs.
Guide
Why?
The code is doing bookkeeping so your brain does not have to keep the entire search space open at once.
- Sorting is used to make local choices comparable, so the later scan does not have to rediscover order.
- A map keeps the lookup side cheap; the code pays a little memory to avoid repeated searching.
- A set is doing the membership or uniqueness work, which keeps the main loop readable.
- The stack stores unfinished context, which is usually the cleanest way to handle nested or monotonic structure.
Guide
How?
Walk through the solution in this order:
- Read the setup variables first.
- Follow the main loop or recursive helper next.
- Watch where invalid states get skipped.
- Check which value survives to the return statement.
The most important competitive-programming habit here is to trust the invariant. Once the invariant is right, the loops become much less scary.
Guide
Complexity
- Time: O(n) to O(n log n), depending on the dominant loop or data structure operation
- Space: O(n) in the usual case for auxiliary containers or recursion
Guide
C++ Solution
Your submission
The accepted solution
01/*
02WA
03success:
04["w","wo","wor","worl","world"]
05["a", "banana", "app", "appl", "ap", "apply", "apple"]
06["m","mo","moc","moch","mocha","l","la","lat","latt","latte","c","ca","cat"]
07["yo","ew","fc","zrc","yodn","fcm","qm","qmo","fcmz","z","ewq","yod","ewqz","y"]
08["ogz","eyj","e","ey","hmn","v","hm","ogznkb","ogzn","hmnm","eyjuo","vuq","ogznk","og","eyjuoi","d"]
09
10fail:
11["rac","rs","ra","on","r","otif","o","onpdu","rsf","rs","ot","oti","racy","onpd"]
12*/
13class Solution {
14public:
15 string longestWord(vector<string>& words) {
16 sort(words.begin(), words.end());
17 copy(words.begin(), words.end(), ostream_iterator<string>(cout, " "));
18 cout << endl;
19 string acc = words[0].size() == 1 ? words[0] : "-";
20 vector<string> candidates;
21 for(int i = 1; i < words.size(); i++){
22 //the accumulation process doesn't start or it ends
23 if(acc == "-" || words[i].size() <= acc.size()){
24 if(acc != "-") candidates.push_back(acc);
25 acc = words[i].size() == 1 ? words[i] : "-";
26 }
27 if(words[i].size() == acc.size()+1 && words[i].rfind(acc, 0) == 0){
28 acc = words[i];
29 }
30 }
31 candidates.push_back(acc);
32 copy(candidates.begin(), candidates.end(), ostream_iterator<string>(cout, " "));
33 cout << endl;
34
35 sort(candidates.begin(), candidates.end(),
36 [](const string& a, const string& b) -> bool{
37 //first sort by length and then by lexicographical order
38 //if length are the same, check their lexicographical order
39 //ow, directly sort by their length
40 return (a.size() == b.size()) ? a < b : a.size() > b.size();
41 }
42 );
43 return candidates[0];
44 }
45};
46
47/*
48Hint 1
49For every word in the input list, we can check whether all prefixes of that word are in the input list by using a Set.
50*/
51//Runtime: 540 ms, faster than 5.02% of C++ online submissions for Longest Word in Dictionary.
52//Memory Usage: 29.4 MB, less than 30.00% of C++ online submissions for Longest Word in Dictionary.
53class Solution {
54public:
55 string longestWord(vector<string>& words) {
56 vector<string> candidates;
57
58 for(string word : words){
59 bool valid = true;
60 for(int i = word.size() - 1; i >= 1; i--){
61 string prefix = word.substr(0, i);
62 // cout << prefix << endl;
63 if(find(words.begin(), words.end(), prefix) == words.end()){
64 valid = false;
65 break;
66 }
67 }
68 if(valid)candidates.push_back(word);
69 }
70 // cout << candidates.size() << endl;
71 // copy(candidates.begin(), candidates.end(), ostream_iterator<string>(cout, " "));
72
73 sort(candidates.begin(), candidates.end(),
74 [](const string& a, const string& b){
75 return a.size() == b.size() ? a < b : a.size() > b.size();
76 }
77 );
78 return candidates[0];
79 }
80};
81
82//Trie + Depth-First Search
83//Runtime: 84 ms, faster than 30.96% of C++ online submissions for Longest Word in Dictionary.
84//Memory Usage: 25.6 MB, less than 50.00% of C++ online submissions for Longest Word in Dictionary.
85class Node {
86public:
87 Node(char c){
88 this->c = c;
89 }
90
91 char c;
92 int end; //default is 0
93 map<char, Node*> children;
94};
95
96class Trie {
97public:
98 Trie() {
99 root = new Node('0');
100 }
101
102 void insert(string word, int index) {
103 Node* cur = root;
104 //put the word into the tree vertically
105 for(char c : word){
106 if(!cur->children[c]){
107 cur->children[c] = new Node(c);
108 }
109 cur = cur->children[c];
110 }
111 //cur->end - 1 is the index for the input array
112 //mark this node comes from input array
113 cur->end = index;
114 }
115
116 string dfs(){
117 string ans = "";
118 stack<Node*> stk;
119 stk.push(root);
120 while(!stk.empty()){
121 Node* node = stk.top(); stk.pop();
122 //root node's end is 0
123 //only want node from input array and root, not a non-leaf node
124 if(node->end > 0 || node == root){
125 //node from input array
126 if(node != root){
127 //node->end -1 is the index for the input array
128 string word = words[node->end -1];
129 if(word.size() > ans.size() ||
130 word.size() == ans.size() && word < ans){
131 ans = word;
132 }
133 }
134 for(auto it = node->children.begin(); it != node->children.end(); it++){
135 stk.push(it->second);
136 }
137 }
138 }
139 return ans;
140 }
141
142 Node* root;
143 vector<string> words;
144};
145
146class Solution {
147public:
148 string longestWord(vector<string>& words) {
149 Trie trie;
150 int index = 0;
151 //put every word in a trie(a prefix tree)
152 for(string word : words){
153 trie.insert(word, ++index);
154 }
155 trie.words = words;
156 //dfs, only searching nodes that ended a word
157 return trie.dfs();
158 }
159};
160
161//set
162//https://leetcode.com/problems/longest-word-in-dictionary/discuss/109114/JavaC%2B%2B-Clean-Code
163//Runtime: 52 ms, faster than 78.87% of C++ online submissions for Longest Word in Dictionary.
164//Memory Usage: 18 MB, less than 80.00% of C++ online submissions for Longest Word in Dictionary.
165class Solution {
166public:
167 string longestWord(vector<string>& words) {
168 unordered_set<string> built;
169 string ans;
170 sort(words.begin(), words.end());
171 for(string word : words){
172 if(word.size() == 1 || built.find(word.substr(0, word.size()-1)) != built.end()){
173 //check both length and their lexicographical order
174 ans = (word.size() == ans.size()) ? (word < ans ? word : ans) : (word.size() > ans.size() ? word : ans);
175 //this works because the vector "words" is sorted
176 // ans = word.size() > ans.size() ? word : ans;
177 //only record the word can be built from one char into the set,
178 //for those word built from 2 or more chars, not record them
179 built.insert(word);
180 }
181 }
182 return ans;
183 }
184};
Cost