Problem
Solution
Hash Set - O(n²) time - O(n) space
This uses two Hash Set structures, one to keep track of the full words and the other to keep track of the prefixes that make up all the words.
- Iterate on the words
- If the word exists in the prefixes set, the set is bad. There’s a word in the list that the current word is a prefix of.
- Iterate on the word characters and build a sliding window substring
- If the substring is in the list of words, the set is bad. There’s a word in the list that is a prefix to the current word.
void printBadSet(string word) {
cout << "BAD SET" << endl;
cout << word << endl;
return;
}
void noPrefix(vector<string> words) {
unordered_set<string> hashWords;
unordered_set<string> prefixes;
for (string word : words) {
if (prefixes.count(word) > 0) {
printBadSet(word);
return;
}
for (int i = 1; i <= word.length(); i++) {
string sub = word.substr(0, i);
if (hashWords.count(sub) > 0) {
printBadSet(word);
return;
} else prefixes.insert(sub);
}
hashWords.insert(word);
}
cout << "GOOD SET" << endl;
}
Trie - [[O(nm)]] time - [[O(nm)]] space
void printBadSet(string word) {
cout << "BAD SET" << endl;
cout << word << endl;
return;
}
struct TrieNode {
TrieNode* children[26] = {};
bool isEnd = false;
};
bool insertCheck(TrieNode* root, const string& word) {
TrieNode* node = root;
for (char c : word) {
int index = c - 'a';
if (!node->children[index]) {
node->children[index] = new TrieNode();
}
node = node->children[index];
// Earlier word is prefix of current word
if (node->isEnd) return true;
}
for (int i = 0; i < 26; i++) {
// Current word is prefix of ealier word
if (node->children[i]) return true;
}
node->isEnd = true;
return false;
}
void noPrefix(vector<string> words) {
TrieNode* root = new TrieNode();
for (string word : words) {
if (insertCheck(root, word)) {
printBadSet(word);
return;
}
}
cout << "GOOD SET" << endl;
}