力扣热题——最长相邻不相等子序列 ||

        题目要求从给定的字符串数组 words 和对应的 groups 数组中,找到一个最长的子序列,满足以下条件:1) 相邻下标的 groups 值不同;2) 相邻字符串长度相等且汉明距离为1。通过动态规划的方法,可以解决该问题。具体步骤包括:初始化 dp 数组记录子序列长度,prev 数组记录前驱节点,遍历所有可能的子序列组合,更新 dpprev 数组,最后根据 prev 数组回溯得到最长子序列。时间复杂度为 O(n² * L),空间复杂度为 O(n + nL),其中 n 是数组长度,L 是字符串的平均长度。

目录

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

题目描述

解法一:

Java写法:

C++写法:

C写法:

运行时间

时间复杂度和空间复杂度

总时间复杂度​​:O(n² * L)

总空间复杂度​​:O(n + nL)

总结


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

注:下述题目描述和示例均来自力扣

题目描述

给你一个整数 n 和一个下标从 0 开始的字符串数组 words ,和一个下标从 0 开始的数组 groups ,两个数组长度都是 n 。

两个长度相等字符串的 汉明距离 定义为对应位置字符 不同 的数目。

你需要从下标 [0, 1, ..., n - 1] 中选出一个 最长子序列 ,将这个子序列记作长度为 k 的 [i0, i1, ..., ik - 1] ,它需要满足以下条件:

  • 相邻 下标对应的 groups 值 不同。即,对于所有满足 0 < j + 1 < k 的 j 都有 groups[ij] != groups[ij + 1] 。
  • 对于所有 0 < j + 1 < k 的下标 j ,都满足 words[ij] 和 words[ij + 1] 的长度 相等 ,且两个字符串之间的 汉明距离 为 1 。

请你返回一个字符串数组,它是下标子序列 依次 对应 words 数组中的字符串连接形成的字符串数组。如果有多个答案,返回任意一个。

子序列 指的是从原数组中删掉一些(也可能一个也不删掉)元素,剩余元素不改变相对位置得到的新的数组。

注意:words 中的字符串长度可能 不相等 。

示例 1:

输入:n = 3, words = ["bab","dab","cab"], groups = [1,2,2]
输出:["bab","cab"]
解释:一个可行的子序列是 [0,2] 。
- groups[0] != groups[2]
- words[0].length == words[2].length 且它们之间的汉明距离为 1 。
所以一个可行的答案是 [words[0],words[2]] = ["bab","cab"] 。
另一个可行的子序列是 [0,1] 。
- groups[0] != groups[1]
- words[0].length = words[1].length 且它们之间的汉明距离为 1 。
所以另一个可行的答案是 [words[0],words[1]] = ["bab","dab"] 。
符合题意的最长子序列的长度为 2 。

示例 2:

输入:n = 4, words = ["a","b","c","d"], groups = [1,2,3,4]
输出:["a","b","c","d"]
解释:我们选择子序列 [0,1,2,3] 。
它同时满足两个条件。
所以答案为 [words[0],words[1],words[2],words[3]] = ["a","b","c","d"] 。
它是所有下标子序列里最长且满足所有条件的。
所以它是唯一的答案。

提示:

  • 1 <= n == words.length == groups.length <= 1000
  • 1 <= words[i].length <= 10
  • 1 <= groups[i] <= n
  • words 中的字符串 互不相同 。
  • words[i] 只包含小写英文字母。

解法一:

1:理解问题要求​

我们需要找到最长的子序列,满足:

  1. 相邻元素的组别不同。
  2. 相邻元素的字符串长度相等且汉明距离为1。
​2:确定动态规划状态​
  • ​定义dp[i]​:表示以第i个元素结尾的最长子序列长度。
  • ​定义prev[i]​:记录子序列中第i个元素的前一个元素下标,用于回溯路径。
​3:状态转移方程​

对于每个元素i,遍历所有前面的元素jj < i),检查:

  1. ​组别不同​​:groups[i] != groups[j]
  2. ​字符串长度相等​​:words[i].length == words[j].length
  3. ​汉明距离为1​​:两字符串对应位置恰好有一个字符不同。

若满足条件,则更新:

如果 dp[j] + 1 > dp[i],则:
    dp[i] = dp[j] + 1
    prev[i] = j
​4:初始化​
  • dp数组初始化为1(每个元素自身构成长度为1的子序列)。
  • prev数组初始化为-1(无前驱节点)。
5:遍历更新​

双重循环遍历所有元素对,更新dpprev数组,同时记录最长子序列的结束位置max_idx

​6:路径回溯​

max_idx开始,通过prev数组逆序回溯路径,得到子序列的索引列表,反转后得到正确顺序。

​7:生成结果​

根据路径索引,从原数组中取出对应字符串,生成最终结果。

Java写法:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Solution {
    public List<String> getWordsInLongestSubsequence(String[] words, int[] groups) {
        int n = words.length;
        int[] dp = new int[n];
        int[] prev = new int[n];
        Arrays.fill(dp, 1);
        Arrays.fill(prev, -1);
        
        int maxLength = 1;
        int maxIndex = 0;
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (groups[i] != groups[j] && words[i].length() == words[j].length() && hammingDistance(words[i], words[j])) {
                    if (dp[j] + 1 > dp[i]) {
                        dp[i] = dp[j] + 1;
                        prev[i] = j;
                    }
                }
            }
            if (dp[i] >= maxLength) {
                maxLength = dp[i];
                maxIndex = i;
            }
        }
        
        List<Integer> pathIndices = new ArrayList<>();
        int current = maxIndex;
        while (current != -1) {
            pathIndices.add(current);
            current = prev[current];
        }
        Collections.reverse(pathIndices);
        
        List<String> result = new ArrayList<>();
        for (int idx : pathIndices) {
            result.add(words[idx]);
        }
        return result;
    }
    
    private boolean hammingDistance(String a, String b) {
        if (a.length() != b.length()) {
            return false;
        }
        int diff = 0;
        for (int i = 0; i < a.length(); i++) {
            if (a.charAt(i) != b.charAt(i)) {
                diff++;
                if (diff > 1) {
                    return false;
                }
            }
        }
        return diff == 1;
    }
}

C++写法:

#include <vector>
#include <string>
#include <algorithm>

using namespace std;

class Solution {
public:
    vector<string> getWordsInLongestSubsequence(vector<string>& words, vector<int>& groups) {
        int n = words.size();
        vector<int> dp(n, 1);
        vector<int> prev(n, -1);
        int max_len = 1, max_idx = 0;

        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (groups[i] != groups[j] && 
                    words[i].size() == words[j].size() &&
                    hammingDistance(words[i], words[j])) {
                    if (dp[j] + 1 > dp[i]) {
                        dp[i] = dp[j] + 1;
                        prev[i] = j;
                    }
                }
            }
            if (dp[i] > max_len) {
                max_len = dp[i];
                max_idx = i;
            }
        }

        vector<int> path;
        for (int cur = max_idx; cur != -1; cur = prev[cur]) {
            path.push_back(cur);
        }
        reverse(path.begin(), path.end());

        vector<string> res;
        for (int idx : path) {
            res.push_back(words[idx]);
        }
        return res;
    }

private:
    bool hammingDistance(const string& a, const string& b) {
        if (a.length() != b.length()) return false;
        int diff = 0;
        for (int i = 0; i < a.length(); ++i) {
            if (a[i] != b[i]) {
                if (++diff > 1) return false;
            }
        }
        return diff == 1;
    }
};

C写法:

#include <stdlib.h>
#include <string.h>

int hammingDistance(char* a, char* b) {
    int lenA = strlen(a);
    int lenB = strlen(b);
    if (lenA != lenB) return 0;
    int diff = 0;
    for (int i = 0; i < lenA; ++i) {
        if (a[i] != b[i]) {
            ++diff;
            if (diff > 1) return 0;
        }
    }
    return diff == 1;
}

char** getWordsInLongestSubsequence(char** words, int wordsSize, int* groups, int groupsSize, int* returnSize) {
    int* dp = (int*)malloc(wordsSize * sizeof(int));
    int* prev = (int*)malloc(wordsSize * sizeof(int));
    for (int i = 0; i < wordsSize; ++i) {
        dp[i] = 1;
        prev[i] = -1;
    }

    int max_len = 1, max_idx = 0;
    for (int i = 0; i < wordsSize; ++i) {
        for (int j = 0; j < i; ++j) {
            if (groups[i] != groups[j] 
                && strlen(words[i]) == strlen(words[j]) 
                && hammingDistance(words[i], words[j])) {
                if (dp[j] + 1 > dp[i]) {
                    dp[i] = dp[j] + 1;
                    prev[i] = j;
                }
            }
        }
        if (dp[i] > max_len) {
            max_len = dp[i];
            max_idx = i;
        }
    }

    // 构建路径
    int* path = (int*)malloc(wordsSize * sizeof(int));
    int current = max_idx;
    int pathSize = 0;
    while (current != -1) {
        path[pathSize++] = current;
        current = prev[current];
    }

    // 反转路径
    for (int i = 0; i < pathSize / 2; ++i) {
        int temp = path[i];
        path[i] = path[pathSize - 1 - i];
        path[pathSize - 1 - i] = temp;
    }

    // 生成结果
    char** result = (char**)malloc(pathSize * sizeof(char*));
    for (int i = 0; i < pathSize; ++i) {
        result[i] = strdup(words[path[i]]);
    }

    *returnSize = pathSize;
    free(dp);
    free(prev);
    free(path);
    return result;
}

运行时间

时间复杂度和空间复杂度

总时间复杂度​​:O(n² * L)
总空间复杂度​​:O(n + nL)


总结

        题目要求从给定的字符串数组 words 和对应的 groups 数组中,找到一个最长的子序列,满足以下条件:1) 相邻下标的 groups 值不同;2) 相邻字符串长度相等且汉明距离为1。通过动态规划的方法,可以解决该问题。具体步骤包括:初始化 dp 数组记录子序列长度,prev 数组记录前驱节点,遍历所有可能的子序列组合,更新 dpprev 数组,最后根据 prev 数组回溯得到最长子序列。时间复杂度为 O(n² * L),空间复杂度为 O(n + nL),其中 n 是数组长度,L 是字符串的平均长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值