Leetcode 2901.最长相邻不相等子序列(C++)

题目

给你一个整数 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.相邻两元素group不同;
2.相邻两元素长度相同但是hamming距离为1;

相对于2900. 最长相邻不相等子序列 I多了第二个限制条件。

这是一个典型的动态规划的题目。

思路

子序列+考虑相邻元素:枚举选哪个。

动态规划本质性的解题步骤:
1.dp数组的定义以及下标的含义
对于本题,定义f[i]表示从下表i~n-1中选出的最短子序列的长度。

2.递推公式(一般是自己找规律得到的)
当满足条件相邻两元素group不同和相邻两元素长度相同但是hamming距离为1时,
若这个f[j]>f[i],说明最长相邻不相等子序列需要更新,则
f[i] = f[j]
from[i] = j
所以最长子序列是max(f)

3.dp数组如何初始化(前提是想清楚1)
设初始值 f[i]=1,表示选择它自己作为子序列。

4.遍历顺序
一维数组,本题无需考虑遍历顺序。可以尝试从0 ~ n-1遍历、从n-1 ~ 0遍历,看看区别。

5.打印dp数组(用于检查是哪里有问题)
可以自行编写代码根据自己的思路进行检查

代码

从n-1~0遍历

class Solution {
public:
    //判断输入是否满足相邻两元素group不同和相邻两元素长度相同但是hamming距离为1
    bool Judge(string &s, string &t) {
        if (s.length() != t.length()) {
            return false;
        }
        bool diff = false;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] != t[i]) {
                if (diff) return false;
                diff = true;
            }
        }
        return diff;
    }
public:
    vector<string> getWordsInLongestSubsequence(int n, vector<string>& words, vector<int>& groups) {
        vector<int> f(n,1);//定义DP数组
        vector<int> from(n);//记录流转过程
        int mx = n - 1;
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 1; j < n; j++) {
                if (f[j] + 1 > f[i] && groups[j] != groups[i] && Judge(words[i], words[j])) {
                    f[i] = f[j] + 1;//i>j,所以从i开始的最长子序列应该更新为从j开始的最长子序列,因为words[i]满足条件,所以最长子序列的长度+1,相当于将words[i]放入结果集中
                    from[i] = j;//从i开始的最长相邻不相等子序列来自从j开始的最长子序列
                }
            }
            //求最长子序列对应的开始字符的下标
            if (f[i] > f[mx]) {
                mx = i;
            }
        }

        int m = f[mx];
        vector<string> ans(m);
        // 追根溯源,一个一个回溯找到ans[i]的值
        for (int i = 0; i < m; i++) {
            ans[i] = words[mx];
            mx = from[mx];
        }
        return ans;
    }
};

从0~n-1遍历

class Solution {
public:
    bool Judge(string &s, string &t) {
        if (s.length() != t.length()) {
            return false;
        }
        bool diff = false;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] != t[i]) {
                if (diff) return false;
                diff = true;
            }
        }
        return diff;
    }
public:
    vector<string> getWordsInLongestSubsequence(int n, vector<string>& words, vector<int>& groups) {
        vector<int> f(n,1);
        vector<int> from(n);
        int mx = n-1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (f[j] + 1 > f[i] && groups[j] != groups[i] && Judge(words[i], words[j])) {
                    f[i] = f[j] + 1;
                    from[i] = j;
                }
            }
            if (f[i] > f[mx]) {
                mx = i;
            }
        }
        int m = f[mx];
        vector<string> ans(m);
        for (int i = m-1; i >= 0; i--) {
            ans[i] = words[mx];
            mx = from[mx];
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值