题目描述
给你一个整数 n
和一个下标从 0 开始的字符串数组 words
,和一个下标从 0 开始的 二进制 数组 groups
,两个数组长度都是 n
。
两个长度相等字符串的 汉明距离 定义为对应位置字符 不同 的数目。
你需要从下标 [0, 1, ..., n - 1]
中选出一个 最长子序列,将这个子序列记作长度为 k
的 [i_0, i_1, ..., i_{k - 1}]
,它需要满足以下条件:
- 对于所有满足
0 < j + 1 < k
的j
都有groups[i_j] != groups[i_{j + 1}]
- 对于所有
0 < j + 1 < k
的下标j
,都满足words[i_j]
和words[i_{j + 1}]
的长度 相等,且两个字符串之间的 汉明距离 为1
。
请你返回一个字符串数组,它是下标子序列 依次 对应 words
数组中的字符串连接形成的字符串数组。如果有多个答案,返回任意一个。
子序列 指的是从原数组中删掉一些(也可能一个也不删掉)元素,剩余元素不改变相对位置得到的新的数组。
注意:words
中的字符串长度可能 不相等。
样例
输入: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。
输入: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
(动态规划) $O(n^2L)$
- 设状态 $f(i)$ 表示以 $i$ 为结尾最长的合法子序列。
- 初始时,$f(i) = 1$。
- 转移时,枚举 $0 \le j < i$,当 $groups(j) \neq groups(i)$ 时且 $words(i)$ 和 $words(j)$ 满足条件时,转移 $f(i) = max(f(i), f(j) + 1)$。同时记录 $i$ 转移前序 $g(i)$。
- 最终子序列的最大长度为 $\max(f(i))$,并从最大 $f(i)$ 的 $i$ 开始回溯转移路径,拼接答案。
时间复杂度
- 动态规划的状态为 $O(n)$,转移时间为 $O(nL)$。
- 构造答案的时间复杂度为 $O(nL)$。
- 故总时间复杂度为 $O(n^2L)$。
空间复杂度
- 需要 $O(nL)$ 的额外空间存储动态规划的状态和答案。
C++ 代码
class Solution {
public:
vector<string> getWordsInLongestSubsequence(int n, vector<string>& words,
vector<int>& groups
) {
vector<int> f(n, 1), g(n, -1);
int res = 1, p = 0;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (groups[j] == groups[i])
continue;
if (words[i].size() != words[j].size())
continue;
int cnt = 0;
for (int k = 0; k < words[i].size(); k++)
if (words[i][k] != words[j][k])
++cnt;
if (cnt != 1)
continue;
if (f[i] < f[j] + 1) {
f[i] = f[j] + 1;
g[i] = j;
}
}
if (res < f[i]) {
res = f[i];
p = i;
}
}
vector<string> ans;
while (p != -1) {
ans.push_back(words[p]);
p = g[p];
}
reverse(ans.begin(), ans.end());
return ans;
}
};