← Home

720. Longest Word in Dictionary

LeetCode article · C++ solution
Website made by wuisabel-gif · Original C++ code by keineahnung2345
trieC++Markdown
720

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:

  1. Read the setup variables first.
  2. Follow the main loop or recursive helper next.
  3. Watch where invalid states get skipped.
  4. 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

solution.cpp
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

Complexity

Time
O(n) to O(n log n), depending on the dominant loop or data structure operation
Dominated by the main traversal, recursion, or data-structure operations in the code.
Space
O(n) in the usual case for auxiliary containers or recursion
Auxiliary state plus the answer structure where the problem requires one.