[LeetCode] 1239. Maximum Length of a Concatenated String with Unique Characters 串联字符串的最大长度
You are given an array of strings arr
. A string s
is formed by the concatenation of a subsequence of arr
that has unique characters.
Return the maximum possible length of s
.
A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements.
Example 1:
Input: arr = ["un","iq","ue"]
Output: 4
Explanation: All the valid concatenations are:
- ""
- "un"
- "iq"
- "ue"
- "uniq" ("un" + "iq")
- "ique" ("iq" + "ue")
Maximum length is 4.
Example 2:
Input: arr = ["cha","r","act","ers"]
Output: 6
Explanation: Possible longest valid concatenations are "chaers" ("cha" + "ers") and "acters" ("act" + "ers").
Example 3:
Input: arr = ["abcdefghijklmnopqrstuvwxyz"]
Output: 26
Explanation: The only string in arr has all 26 characters.
Example 4:
Input: arr = ["aa","bb"]
Output: 0
Explanation: Both strings in arr do not have unique characters, thus there are no valid concatenations.
Constraints:
1 <= arr.length <= 16
1 <= arr[i].length <= 26
arr[i]
contains only lowercase English letters.
这道题给了一个字符串数组 arr,让找出一个最长的串联字符串使得其没有重复字符。其实这道题有点背包问题的感觉,每个字符串就相当于一个物品,可以选择拿与不拿,限制条件是不能有重复的字符,然后求可以拿到的最长的字符串的长度。实际上最终符合要求的最长串联字符串一定是原字符串数组的某个子集合,那么一个比较直接的办法就是遍历所有的子集合,然后把它们都串联起来,并且判断是否有重复的字符。
这里可以用 DFS 来做,递归子函数除了原数组 arr 之外,再输入两个变量 cur 和 idx,其中 cur 表示当前串联成的字符串,idx 表示遍历到 arr 数组中的位置。在递归函数中,首先要判断 cur 中是否有重复的字符,一个比较快速的方法就是将其放到一个 HashSet 中,利用其的自动去重复的特性,若有重复字符,则其中元素的个数一定小于原字符串到个数。若二者相等,则可以进行下一步操作,从 idx 开始遍历原数组 arr,将新的 arr[i] 加到 cur 上,并调用递归函数,将 cur+arr[i] 和 i+1 传入,并将返回值用来更新结果 res 即可,参见代码如下:
解法一:
class Solution {
public:
int maxLength(vector<string>& arr) {
return dfs(arr, "", 0);
}
int dfs(vector<string>& arr, string cur, int idx) {
unordered_set<char> st(cur.begin(), cur.end());
if (st.size() != cur.size()) return 0;
int res = cur.size();
for (int i = idx; i < arr.size(); ++i) {
res = max(res, dfs(arr, cur + arr[i], i + 1));
}
return res;
}
};
上面的方法虽然可以通过 OJ,但是不算特别高效,因为当字符串很长的时候,将其转为 HashSet 就比较耗时。这里可以用一个高级的数据结构位图 bitset 来做,大小设置为 26,每一位代表一个字母,因为题目限定了只有小写字母。这里新建一个位图的数组,初始时将一个空的 bitset 加入。然后遍历数组 arr 中的每个字符串,将遍历到的字符串加到一个新建的位图 cur 中,这时候就可以利用 bitset 的 count 函数来快速知道有多少个不重复的字符了,跟上面解法中的 HashSet 的作用很像。还是根据不同字符的个数跟原字符串的长度做个比较,若不同直接跳过剩余操作,做个剪枝处理。否则就从 all 位图数组的末尾开始往前遍历,判断遍历到的位图t和当前位图 cur 是否有交集,有的话说明有重复字符,直接跳过,否则就将二者合并,并放到 all 的末尾,并将合并后的长度用来更新结果 res 即可,参见代码如下:
解法二:
class Solution {
public:
int maxLength(vector<string>& arr) {
vector<bitset<26>> all{bitset<26>()};
int res = 0;
for (string word : arr) {
bitset<26> cur;
for (char c : word) {
cur.set(c - 'a');
}
int n = cur.count();
if (n < word.size()) continue;
for (int i = (int)all.size() - 1; i >= 0; --i) {
bitset<26> t = all[i];
if ((t & cur).any()) continue;
all.push_back(t | cur);
res = max(res, (int)t.count() + n);
}
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1239
参考资料:
https://leetcode.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters/