##### Welcome to Subscribe On Youtube

Formatted question description: https://leetcode.ca/all/1048.html

# 1048. Longest String Chain (Medium)

Given a list of words, each word consists of English lowercase letters.

Let's say word1 is a predecessor of word2 if and only if we can add exactly one letter anywhere in word1 to make it equal to word2.  For example, "abc" is a predecessor of "abac".

A word chain is a sequence of words [word_1, word_2, ..., word_k] with k >= 1, where word_1 is a predecessor of word_2, word_2 is a predecessor of word_3, and so on.

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

Example 1:

Input: ["a","b","ba","bca","bda","bdca"]
Output: 4
Explanation: one of the longest word chain is "a","ba","bda","bdca".


Note:

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

Related Topics:
Hash Table, Dynamic Programming

## Solution 1.

• class Solution {
public int longestStrChain(String[] words) {
Arrays.sort(words, new Comparator<String>() {
public int compare(String str1, String str2) {
if (str1.length() != str2.length())
return str1.length() - str2.length();
else
return str1.compareTo(str2);
}
});
int smallestLength = words[0].length();
int nextLength = smallestLength + 1;
int length = words.length;
int[] dp = new int[length];
int dpStartIndex = -1;
for (int i = 0; i < length; i++) {
dp[i] = 1;
if (words[i].length() == nextLength && dpStartIndex < 0)
dpStartIndex = i;
}
if (dpStartIndex < 0)
return 1;
for (int i = dpStartIndex; i < length; i++) {
String curWord = words[i];
for (int j = i - 1; j >= 0; j--) {
String prevWord = words[j];
if (prevWord.length() == curWord.length())
continue;
else if (prevWord.length() < curWord.length() - 1)
break;
else {
if (isSubsequence(prevWord, curWord))
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
int maxLength = 0;
for (int num : dp)
maxLength = Math.max(maxLength, num);
return maxLength;
}

public boolean isSubsequence(String str1, String str2) {
int length1 = str1.length(), length2 = str2.length();
if (length1 > length2)
return false;
int index1 = 0, index2 = 0;
while (index1 < length1 && index2 < length2) {
char c1 = str1.charAt(index1), c2 = str2.charAt(index2);
if (c1 == c2)
index1++;
index2++;
}
return index1 == length1;
}
}

############

class Solution {
public int longestStrChain(String[] words) {
Arrays.sort(words, Comparator.comparingInt(String::length));
int res = 0;
Map<String, Integer> map = new HashMap<>();
for (String word : words) {
int x = 1;
for (int i = 0; i < word.length(); ++i) {
String pre = word.substring(0, i) + word.substring(i + 1);
x = Math.max(x, map.getOrDefault(pre, 0) + 1);
}
map.put(word, x);
res = Math.max(res, x);
}
return res;
}
}

• // OJ: https://leetcode.com/problems/longest-string-chain/
// Time: O(N * S^2)
// Space: O(NS)
class Solution {
unordered_map<string, int> m;
int dp(const string &s) {
if (m[s]) return m[s];
if (s.size() == 1) return 1;
int ans = 1;
for (int i = 0; i < s.size(); ++i) {
auto copy = s;
copy.erase(begin(copy) + i);
if (m.count(copy)) {
ans = max(ans, 1 + dp(copy));
}
}
return m[s] = ans;
}
public:
int longestStrChain(vector<string>& A) {
for (auto &s : A) m[s] = 0;
int ans = 0;
for (const auto &[s, len] : m) {
ans = max(ans, dp(s));
}
return ans;
}
};

• class Solution:
def longestStrChain(self, words: List[str]) -> int:
def check(w1, w2):
if len(w2) - len(w1) != 1:
return False
i = j = cnt = 0
while i < len(w1) and j < len(w2):
if w1[i] != w2[j]:
cnt += 1
else:
i += 1
j += 1
return cnt < 2 and i == len(w1)

n = len(words)
dp = [1] * (n + 1)
words.sort(key=lambda x: len(x))
res = 1
for i in range(1, n):
for j in range(i):
if check(words[j], words[i]):
dp[i] = max(dp[i], dp[j] + 1)
res = max(res, dp[i])
return res

############

# 1048. Longest String Chain
# https://leetcode.com/problems/longest-string-chain/

class Solution:
def longestStrChain(self, words: List[str]) -> int:
dp = {}
res = 1

for word in sorted(words, key = len):
dp[word] = 1

for i in range(len(word)):
prev = word[:i] + word[i + 1:]

if prev in dp:
dp[word] = max(dp[word], dp[prev] + 1)
res = max(res, dp[word])

return res


• func longestStrChain(words []string) int {
sort.Slice(words, func(i, j int) bool { return len(words[i]) < len(words[j]) })
res := 0
mp := make(map[string]int)
for _, word := range words {
x := 1
for i := 0; i < len(word); i++ {
pre := word[0:i] + word[i+1:len(word)]
x = max(x, mp[pre]+1)
}
mp[word] = x
res = max(res, x)
}
return res
}

func max(a, b int) int {
if a > b {
return a
}
return b
}

• function longestStrChain(words: string[]): number {
words.sort((a, b) => a.length - b.length);
let ans = 0;
let hashTable = new Map();
for (let word of words) {
let c = 1;
for (let i = 0; i < word.length; i++) {
let pre = word.substring(0, i) + word.substring(i + 1);
c = Math.max(c, (hashTable.get(pre) || 0) + 1);
}
hashTable.set(word, c);
ans = Math.max(ans, c);
}
return ans;
}