寻找满足特定条件的最长子序列题解

题目链接2901. 最长相邻不相等子序列 II - 力扣(LeetCode)

一、题目分析​

给定一个整数 n,以及长度均为 n 的字符串数组 words 和数组 groups。我们的目标是从下标 [0, 1, ..., n - 1] 中找出一个最长子序列,这个子序列需满足两个条件:​

  1. 相邻下标对应的 groups 值不同,即对于所有满足 0 < j + 1 < k 的 j,都有 groups[j] != groups[j + 1]。​
  1. 对于所有 0 < j + 1 < k 的下标 j,words[j] 和 words[j + 1] 的长度相等,且两个字符串之间的汉明距离为 1。汉明距离是指对应位置字符不同的数目。最后需要返回该下标子序列依次对应 words 数组中的字符串连接形成的字符串数组。​

二、解题思路​

本题可以使用动态规划(Dynamic Programming,DP)的方法来解决。核心思路是通过记录以每个位置为结尾的最长子序列长度,逐步推导出全局最长子序列。​

  1. 初始化:​
  • 定义 dp 数组,dp[i] 表示以 i 为结尾的最长子序列长度,初始化为 1。​
  • 定义 pre 数组,pre[i] 用于记录最长子序列中 i 的前一个位置,初始化为 -1。​
  • 定义 maxIndex 用于记录最长子序列的最后一个位置,初始化为 -1。​
  1. 动态规划过程:​
  • 外层循环遍历 words 数组的每个位置 i。​
  • 内层循环从 i - 1 倒序遍历到 0,对于每个 j,判断是否满足两个条件:​
  • groups[i] != groups[j],即相邻位置的 groups 值不同。​
  • checkWord(words[i], words[j]),即 words[i] 和 words[j] 长度相等且汉明距离为 1。如果满足条件,且 dp[j] + 1 > dp[i],则更新 dp[i] 和 pre[i]。​
  • 每次遍历完 i 后,更新 maxIndex,使其指向当前找到的最长子序列的最后一个位置。​
  1. 构造结果:​
  • 从 maxIndex 开始,通过 pre 数组回溯,将对应的 words 元素添加到结果数组 res 中。​
  • 最后将 res 数组反转,得到正确顺序的最长子序列对应的字符串数组。​

三、代码实现与详解​

class Solution {
public:
    // 主函数,用于获取满足条件的最长子序列对应的字符串数组
    vector<string> getWordsInLongestSubsequence(vector<string>& words,
                                                vector<int>& groups) {
        int n = words.size();
        // dp[i] 表示以 i 为结尾的最长子序列长度,初始化为 1
        vector<int> dp(n, 1);
        // pre[i] 用于记录最长子序列中 i 的前一个位置,初始化为 -1
        vector<int> pre(n, -1);
        int maxIndex = -1;
        // 外层循环遍历 words 数组的每个位置 i
        for (int i = 0; i < n; i++) {
            // 内层循环从 i - 1 倒序遍历到 0
            for (int j = i - 1; j >= 0; j--) {
                if (dp[j] >= dp[i]) {
                    // 判断相邻位置的 groups 值不同,且 words[i] 和 words[j] 满足条件
                    if (groups[i] != groups[j] &&
                        checkWord(words[i], words[j])) {
                        if (dp[j] + 1 > dp[i]) {
                            // 更新 dp[i] 和 pre[i]
                            dp[i] = dp[j] + 1;
                            pre[i] = j;
                        }
                    }
                }
            }

            if (maxIndex < 0 || dp[i] > dp[maxIndex]) {
                // 更新 maxIndex 为当前最长子序列的最后一个位置
                maxIndex = i;
            }
        }

        vector<string> res;
        // 从 maxIndex 开始,通过 pre 数组回溯,将对应的 words 元素添加到结果数组 res 中
        for (int i = maxIndex; i >= 0; i = pre[i]) {
            res.emplace_back(words[i]);
        }
        // 反转 res 数组,得到正确顺序的最长子序列对应的字符串数组
        reverse(res.begin(), res.end());
        return res;
    }

    // 检查两个字符串是否长度相等且汉明距离为 1
    bool checkWord(string s1, string s2) {
        if (s1.size() != s2.size()) {
            return false;
        }
        int count = 0;
        int n = s1.size();
        // 遍历字符串,统计不同字符的个数
        for (int i = 0; i < n; i++) {
            if (s1[i] != s2[i]) {
                count++;
                if (count > 1) {
                    return false;
                }
            }
        }
        return count == 1;
    }
};

代码说明​

  1. getWordsInLongestSubsequence 函数:​
  • 首先初始化 dp、pre 数组和 maxIndex。​
  • 通过两层循环进行动态规划,更新 dp 和 pre 数组,并找到最长子序列的最后一个位置 maxIndex。​
  • 最后通过回溯和反转操作,得到满足条件的最长子序列对应的字符串数组。​
  1. checkWord 函数:​
  • 用于判断两个字符串是否长度相等且汉明距离为 1。通过遍历字符串,统计不同字符的个数来实现判断。​

四、时间与空间复杂度分析​

  1. 时间复杂度:​动态规划部分的时间复杂度主要由两层循环决定,外层循环 n 次,内层循环最多 n 次,所以动态规划部分时间复杂度为 ​O(n^2)。​checkWord 函数中遍历字符串的时间复杂度为 ​O(m),其中 m 为字符串的长度。由于在动态规划中每次调用 checkWord,所以总的时间复杂度为 ​O(n^2 * m),其中 n 为 words 数组的长度,m 为字符串的平均长度。
  2. 空间复杂度:使用了 dp 和 pre 两个长度为 n 的数组,以及结果数组 res 最多存储 n 个元素,所以空间复杂度为 ​O(n)。​
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

.wei-upup

如若对您有用,盼您赏个鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值