Problem description:

You are given an array of words where each word consists of lowercase English letters.

wordA is a predecessor of wordB if and only if we can insert exactly one letter anywhere in wordA without changing the order of the other characters to make it equal to wordB.

  • For example, "abc" is a predecessor of "abac", while "cba" is not a predecessor of "bcad".

A word chain is a sequence of words [word1, word2, ..., wordk] with k >= 1, where word1 is a predecessor of word2, word2 is a predecessor of word3, and so on. A single word is trivially a word chain** with k == 1.

Return the length of the longest possible word chain with words chosen from the given list of words.

Example 1:

1
2
3
Input: words = ["a","b","ba","bca","bda","bdca"]
Output: 4
Explanation: One of the longest word chains is ["a","ba","bda","bdca"].

Example 2:

1
2
3
Input: words = ["xbc","pcxbcf","xb","cxbc","pcxbc"]
Output: 5
Explanation: All the words can be put in a word chain ["xb", "xbc", "cxbc", "pcxbc", "pcxbcf"].

Example 3:

1
2
3
4
Input: words = ["abcd","dbqca"]
Output: 1
Explanation: The trivial word chain ["abcd"] is one of the longest word chains.
["abcd","dbqca"] is not a valid word chain because the ordering of the letters is changed.

Constraints:

  • 1 <= words.length <= 1000
  • 1 <= words[i].length <= 16
  • words[i] only consists of lowercase English letters.

Solution:

The idea is to find all the chain we could get from shortest string to longest string

  1. sort the words based on length
  2. find if the substring in the word have shown up in the map
    1. if it’s in map, could make a longer chain
    2. if not, ignore it
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def longestStrChain(self, words: List[str]) -> int:
res = 0
dp = {}
words.sort(key=len)

for word in words:
# break down every substring, find if it exist in map already
# in map: 1+dp[substring]
# not in map: ignore
dp[word] = 1
for i in range(len(word)):
substring = word[:i]+word[i+1:]
# dp[word] = max(dp[word], dp.get(substring, 0)+1)
if substring in dp:
dp[word] = max(dp[word], 1+dp[substring])
res = max(res, dp[word])
return res

time complexity: $O(nlogn)$ for sorting, $O(n*l^2)$ in for loop
space complexity: $O(nl)$
reference:
related problem: