class Solution {
public:
// Iterate through tree, where every node is a word. And every node's children are the set of predecessors associated with that word.
int dfsGetHeight(const string& word, unordered_map<string, unordered_set<string>>& setOfPredecessorsPerWord)
{
int maxH = 0;
for(const string& successor : setOfPredecessorsPerWord[word])
{
int height = dfsGetHeight(successor, setOfPredecessorsPerWord);
maxH = std::max(maxH, height);
}
return maxH + 1;
}
int longestStrChain(vector<string>& words) {
// Group the words by their length.
unordered_map<int, unordered_set<string>> wordsPerLength{};
for(const string& word : words)
{
if(wordsPerLength.find(word.size()) == wordsPerLength.end())
{
wordsPerLength.insert({word.size(), unordered_set<string>{}});
}
wordsPerLength[word.size()].insert(word);
}
// Every unique word has a set of predecessors (could be the empty set).
unordered_map<string, unordered_set<string>> setOfPredecessorsPerWord{};
for(const string& word : words)
{
if(setOfPredecessorsPerWord.find(word) == setOfPredecessorsPerWord.end())
{
setOfPredecessorsPerWord.insert({word, unordered_set<string>{}});
}
}
unordered_set<string> wordIsSeen{};
// Add predecessors to each word's predecessorSet.
for(const string& word : words)
{
// Don't do the same word twice. Already found the predecessors for this word.
if(wordIsSeen.find(word) != wordIsSeen.end())
{
continue;
}
wordIsSeen.insert(word);
// If there are no words with a length of one smaller than the word, then continue to next word.
if(wordsPerLength.find(word.size()-1) == wordsPerLength.end())
{
continue;
}
// Check every word with the correct length (that's word.size()-1) to see if it's a predecessor.
for(string shortWord : wordsPerLength[word.size()-1])
{
// short word's index
int s_ii = 0;
// w_ii is the word's index
int w_ii = 0;
while(w_ii < word.size() && s_ii < shortWord.size())
{
// if the letter's don't match, skip this letter in word.
if(word[w_ii] != shortWord[s_ii])
{
++w_ii;
}
else
{
++s_ii;
++w_ii;
}
// if already skipped more than 1 letter, then it's not a predecessor, break.
if ( (w_ii-s_ii) > 1)
{
break;
}
}
// It's a predecessor if zero or more letters were skipped.
if (w_ii-s_ii <= 1)
{
setOfPredecessorsPerWord[word].insert(shortWord);
}
}
}
vector<int> wordLengths{};
for(auto& lengthAndWords : wordsPerLength)
{
wordLengths.push_back(lengthAndWords.first);
}
sort(wordLengths.begin(), wordLengths.end(), [](const int& a, const int& b){ return b < a;});
// length of longest word chain
int maxWC = 0;
// Go through each word, but do the long words first (using sorted wordLengths).
for(int length : wordLengths)
{
// if the length of these words are smaller than the longest word chain, then there's no chance that the word will make a longer word chain than maxWC.
// The longest word chain a word can make is it's length.
if(length < maxWC)
{
break;
}
for(auto& word : wordsPerLength[length])
{
int height = dfsGetHeight(word, setOfPredecessorsPerWord);
maxWC = std::max(maxWC, height);
}
}
return maxWC;
}
};