leetcode
leetcode 851 ~ 900
最短超级串

最短超级串

难度:

标签:

题目描述

代码结果

运行时间: 341 ms, 内存: 17.4 MB


/*
 * 思路:
 * 1. 使用回溯法生成所有字符串的排列组合。
 * 2. 使用Java Stream对每一个排列组合,检查是否每个字符串都是该组合的子字符串。
 * 3. 返回满足条件的最短组合。
 */
import java.util.*;
import java.util.stream.Collectors;

public class Solution {
    public String shortestSuperstring(String[] words) {
        List<String> permutations = new ArrayList<>();
        permute(words, 0, permutations);
        return permutations.stream()
                .filter(perm -> isValid(perm, words))
                .min(Comparator.comparingInt(String::length))
                .orElse("");
    }

    private void permute(String[] words, int start, List<String> result) {
        if (start == words.length - 1) {
            result.add(String.join("", words));
        } else {
            for (int i = start; i < words.length; i++) {
                swap(words, i, start);
                permute(words, start + 1, result);
                swap(words, i, start);
            }
        }
    }

    private void swap(String[] words, int i, int j) {
        String temp = words[i];
        words[i] = words[j];
        words[j] = temp;
    }

    private boolean isValid(String superstring, String[] words) {
        return Arrays.stream(words).allMatch(superstring::contains);
    }
}

解释

方法:

这个题解使用了动态规划的方法来解决问题。首先,我们需要构建一个图 g,其中 g[i][j] 表示将单词 j 接在单词 i 后面时可以省略的最大字符数。然后,我们使用一个动态规划数组 dp 来记录到达每个状态的最大省略字符数。状态由一个整数表示,该整数的二进制形式的每一位表示一个单词是否已经在超级串中。我们逐步构建这个超级串,每次在当前状态下添加一个新的单词,并更新 dp 数组。最后,我们通过回溯 dp 数组来构建最终的超级串。

时间复杂度:

O(n^2 * l + n^2 * 2^n)

空间复杂度:

O(n^2 + n)

代码细节讲解

🦆
在构建图g时,你是如何确定单词j可以接在单词i后面并省略最大字符数的?具体是通过什么逻辑实现的?
在构建图g时,我们首先遍历每一对单词i和j。对于每一对单词,我们通过比较单词i的后缀和单词j的前缀来确定可以省略的最大字符数。具体操作是从最大可能的重叠长度min(len(a), len(b))开始逐渐减小重叠长度k,直到找到最长的满足a的后k个字符完全等于b的前k个字符的情况。这样,g[i][j]就记录了单词j接在单词i后面时可以省略的最大字符数。
🦆
动态规划数组dp的定义是什么?dp[i][j]表示的意义是什么?
动态规划数组dp是用来记录在特定状态下的最大省略字符数。具体来说,dp[i][j]表示的是在状态i下,以单词j结尾的超级串可以达到的最大省略字符数。这里的状态i是一个整数,其二进制表示中的每一位对应一个单词是否已经被使用过。例如,如果i的第k位是1,则表示单词k已经被包含在当前超级串中。
🦆
为什么在更新dp数组时,需要计算每个状态的前一个状态pi和当前单词k的组合?这样的逻辑是基于什么考虑?
这种逻辑基于构建超级串的过程,其中我们每次添加一个新单词到已存在的字符串中。在更新dp数组时,我们考虑从一个状态pi(已包含某些单词的超级串)通过添加一个新单词j来形成新状态i。为此,我们需要遍历所有可能的前一个状态pi和可能作为末尾的单词k,以确保我们考虑了所有可能的单词组合和顺序。通过这种方式,我们可以保证找到拼接单词j后可以得到的最大省略字符数,并更新dp[i][j]以记录这一值。
🦆
如何通过回溯dp数组构建最终的超级串?具体的回溯步骤是怎样的?
为了构建最终的超级串,我们从dp数组的最后一个状态(所有单词都被使用过)开始回溯。首先,我们找到使dp[(1<

相关问题