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 &gt;= 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.

&nbsp;
Example 1:

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


Example 2:

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:

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.


&nbsp;
Constraints:


	1 &lt;= words.length &lt;= 1000
	1 &lt;= words[i].length &lt;= 16
	words[i] only consists of lowercase English letters.


class Solution:
    def longestStrChain(self, words: List[str]) -> int:
        
        words.sort(key=len)
        dic = {}
        
        for i in words:
            dic[ i ] = 1
            
            for j in range(len(i)):
                
                # creating words by deleting a letter
                successor = i[:j] + i[j+1:]
                if successor in dic:
                    dic[ i ] = max (dic[i], 1 + dic[successor])
        
        res = max(dic.values())
        return res
class Solution {
    
    Boolean compareForIncreaseByOne(String str1,String str2){
            //str 1 will be long than str2
        int first=0;
        int second=0;
        if(str1.length() != (str2.length() + 1)){
            return false;
        }
        while(first < str1.length()){
            if(second < str2.length() && str1.charAt(first) == str2.charAt(second)){
                first++;
                second++;
            }else{
                first++;
            }
        }
        if(first == str1.length() && second == str2.length()){
            return true;
        }
        return false;
    }
    
    public int longestStrChain(String[] words) {
        int N = words.length;
        Arrays.sort(words,(a,b) -> a.length()-b.length());  //as Sequence/Subset are not ordered
        int []dp =new int[N];
        Arrays.fill(dp,1);
        int maxi = 1;
        for(int i=0;i<N;i++){
            for(int j=0;j<i;j++){
                if(compareForIncreaseByOne(words[i],words[j]) && dp[j]+1 > dp[i]){
                    dp[i] = dp[j] + 1;
                    maxi = Math.max(maxi,dp[i]);
                }
            }
        }//for neds
        return maxi;
    }
}
class Solution {
    int dfs(unordered_map<string, int>& m, unordered_set<string>& setWords, 
            const string& w) {
        if (m.count(w)) return m[w];
        int maxLen = 1;
        
        for(int i=0;i<w.size();++i) {
            auto preWord = w.substr(0, i) + w.substr(i+1);
            if(setWords.count(preWord)) {
                int newLen = 1 + dfs(m, setWords, preWord);
                maxLen = max(newLen, maxLen);
            }
        }
        m[w] = maxLen;
        return maxLen;
        
    }
public:
    int longestStrChain(vector<string>& words) {
        
        unordered_map<string, int> m;
        unordered_set<string> setWords(words.begin(), words.end());
        int res = 0;
		
        for(auto& w:words) {
            res = max(res, dfs(m, setWords, w));
        }
        return res;
    }
};
/**
 * @param {string[]} words
 * @return {number}
 */
var longestStrChain = function(words) 
{
        let tiers = new Array(16);
        for(let i=0; i<tiers.length; i++)
                tiers[i] = [];
        for(let word of words)
                tiers[word.length-1].push({word,len:1});

        const isPredecessor = function(word1,word2)     // Assumes word2.length = word1.length+1
        {
                let w1p = 0, misses = 0;
                for(let w2p = 0; w2p < word2.length; w2p++)
                {
                        if(word2[w2p] !== word1[w1p])
                        {
                                if(misses === 1)
                                        return false;
                                misses = 1;
                        }
                        else
                        {
                                w1p++;
                                if(w1p === word1.length)
                                        return true;
                        }
                }
                return true;
        };
        
        
        for(let i=tiers.length-1; i>0; i--)
        {
                for(let w2=0; w2<tiers[i].length; w2++)
                {
                        for(let w1 = 0; w1 < tiers[i-1].length; w1++)
                        {
                                if(tiers[i-1][w1].len >= tiers[i][w2].len+1)
                                        continue;
                                if(isPredecessor(tiers[i-1][w1].word, tiers[i][w2].word))
                                        tiers[i-1][w1].len = tiers[i][w2].len+1;
                        }
                }
        }
        
        let max = 0;
        for(let i=0; i<tiers.length; i++)
        {
                for(let j=0; j<tiers[i].length; j++)
                        max = Math.max(max,tiers[i][j].len);
        }
        
        return max;
};
Longest String Chain