LC-1048. 最长字符串链(贪心+枚举,记忆化搜索==>动态规划)

1048. 最长字符串链

难度中等225

给出一个单词数组 words ,其中每个单词都由小写英文字母组成。

如果我们可以 不改变其他字符的顺序 ,在 wordA 的任何地方添加 恰好一个 字母使其变成 wordB ,那么我们认为 wordAwordB前身

  • 例如,"abc""abac"前身 ,而 "cba" 不是 "bcad"前身

词链是单词 [word_1, word_2, ..., word_k] 组成的序列,k >= 1,其中 word1word2 的前身,word2word3 的前身,依此类推。一个单词通常是 k == 1单词链

从给定单词列表 words 中选择单词组成词链,返回 词链的 最长可能长度

示例 1:

输入:words = ["a","b","ba","bca","bda","bdca"]
输出:4
解释:最长单词链之一为 ["a","ba","bda","bdca"]

示例 2:

输入:words = ["xbc","pcxbcf","xb","cxbc","pcxbc"]
输出:5
解释:所有的单词都可以放入单词链 ["xb", "xbc", "cxbc", "pcxbc", "pcxbcf"].

示例 3:

输入:words = ["abcd","dbqca"]
输出:1
解释:字链["abcd"]是最长的字链之一。
["abcd","dbqca"]不是一个有效的单词链,因为字母的顺序被改变了。

提示:

  • 1 <= words.length <= 1000
  • 1 <= words[i].length <= 16
  • words[i] 仅由小写英文字母组成。

贪心 + 模拟(做题第一步先看数据范围)

根据数据范围猜解法 ==> O(n^2)

贪心:字符串词链长度一定是从小到大变长的,可以从短的字符串开始计算是否有前身,长的字符串的前身一定是从短的字符串转移来的

字符串数组根据长度和字典序进行排序,然后暴力枚举word的每一个前身是否存在,若存在前身,word的词链长度 = 前身词链长度 + 1,最后返回最大值

class Solution {
    public int longestStrChain(String[] words) {
        Arrays.sort(words, (a, b) -> a.length() == b.length() ? a.compareTo(b) : a.length() - b.length());
        int res = 1;
        Map<String, Integer> map = new HashMap<>();
        for(String w : words){
            int len = 0;
            int n = w.length();
            for(int i = 0; i < n; i++){
                String key = w.substring(0, i) + w.substring(i+1);
                if(map.containsKey(key)){
                    len = Math.max(len, map.get(key));
                }
            }
            map.put(w, len+1);
            res = Math.max(res, len+1);
        }
        return res;
    }
}

动态规划

模拟中中间存在枚举的过程

  • 存在则长度=前身+1

  • 不存在则长度=1

可以用动态规划来求解

https://leetcode.cn/problems/longest-string-chain/solution/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-wdkm/

对于字符串 s 来说,假设它是词链的最后一个单词,那么去掉 s 中的一个字母,设新字符串为 t,问题就变成计算以 t 结尾的词链的最长长度。由于这是一个和原问题相似的子问题,因此可以用递归解。

记忆化搜索:

class Solution {
    Map<String, Integer> ws = new HashMap<>();
    public int longestStrChain(String[] words) {
        // 为快速判断字符串是否在words中,需要将所有字符串存入哈希表中
        for(String s : words){
            ws.put(s, 0); // 0表示未被计算
        }
        int res = 0;
        for(String s : ws.keySet()){
            res = Math.max(res, dfs(s));
        }
        return res;

    }
    // 定义dfs(s)表示以s结尾的词链的最长长度 
    public int dfs(String s){
        int res = ws.get(s);
        if(res > 0) return res; // 之前计算过
        for(int i = 0; i < s.length(); i++){
            // 由于字符串长度不超过16,暴力枚举去掉的字符
            String t = s.substring(0, i) + s.substring(i+1);
            if(ws.containsKey(t)){// t 在 words 中
                res = Math.max(res, dfs(t));
            }
        }
        ws.put(s, res + 1); // 记忆化
        return res+1;
    }
}

转成递推:

对于本题,只需要把递归改成循环。

由于我们总是从短的字符串转移到长的字符串,所以要先把字符串按长度从小到大排序,然后从短的开始递推。

  • 震惊:居然和 贪心 + 枚举 的解法一样
class Solution {
    public int longestStrChain(String[] words) {
        Arrays.sort(words, (a, b) -> a.length() - b.length());
        int res = 0;
        Map<String, Integer> map = new HashMap<>();
        for(String w : words){
            int len = 0;
            for(int i = 0; i < w.length(); i++){
                String key = w.substring(0, i) + w.substring(i+1);
                len = Math.max(len, map.getOrDefault(key, 0));
            }
            map.put(w, len+1);
            res = Math.max(res, len+1);
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值