题目
给定一个字符串数组 arr,字符串 s 是将 arr 某一子序列字符串连接所得的字符串,如果 s 中的每一个字符都只出现过一次,那么它就是一个可行解。
请返回所有可行解 s 中最长长度。
示例 1:
输入:arr = ["un","iq","ue"]
输出:4
解释:所有可能的串联组合是 "","un","iq","ue","uniq" 和 "ique",最大长度为 4。
示例 2:
输入:arr = ["cha","r","act","ers"]
输出:6
解释:可能的解答有 "chaers" 和 "acters"。
示例 3:
输入:arr = ["abcdefghijklmnopqrstuvwxyz"]
输出:26
提示:
1 <= arr.length <= 16
1 <= arr[i].length <= 26
arr[i] 中只含有小写英文字母
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析(回溯法)
回溯法的思想:在搜索的过程中尝试问题的解,每次搜索过程中都会出现一个可选集,无法确认可选集中哪个元素属于最优解,当当前的解不是最优解时能够回溯到可选集中选择另一个元素再次进行搜索。有点类似DFS(深度优先遍历)。
回溯算法的题做的比较少,还不是很熟练,了解了其基本思想后需要练习相关习题尽快熟悉。
该题算是回溯法比较经典的题目,在数组未完成遍历前无法得知当前选择的字符串是否为最大长度,若选择的不是最大长度则需要回溯到可选结果集重新选择。
- 根据题意,我们需要一种方法,当前字符串与以组成的字符串的字符是否包含相同字符。我们可以使用bit位来进行判断。
一个int类型有32个bit位,除去最高位符号位,则可用bit位为31位。
- 字符串中只包含26个小写字母,则用1个int类型可保存我们组成的字符串
intStr
。 - 每遍历1个字符串,都将其中的字符与
intStr
做&(and)
操作,若该字符与intStr
中的某个字符相同,则结果一定不为0; - 若所有字符都与
intStr
中的字符不重复,则该串可用,将该串的字符与intStr
做|(or)
操作,结果保存到intStr
,目的是将新串字符加入到intStr
。 - 若当前字符串与已组成的字符串重复,则跳过该字符串,继续判断下一字符串是否可用。
- 根据以上思路还不足以解答出该问题,下面才是最重要的回溯法,由于从前往后遍历,无法确认前面加入的字符串长度一定大于后面不可用的字符串。所以还需要计算不添加当前字符串方式的最终长度,与添加当前字符串方式的最终长度相比,取最大长度即可。
代码
class Solution {
public:
int maxLength(vector<string>& arr) {
return backTrace(arr, 0, 0);
}
int backTrace(vector<string> &arr, int i, int bit)
{
if (arr.size() == i) {
return 0;
}
int tmp = bit;
for (char c : arr[i]) {
if ((bit & (1 << (c - 'a'))) != 0) {
// 计算bit位是否满足条件,若不满足则直接跳转到下一个字符串
return backTrace(arr, i + 1, tmp);
}
// 若没有重复的字符则将字符加入到bit中
bit |= (1 << (c - 'a'));
}
int len = arr[i].size();
/**
* len + backTrace(arr, i + 1, bit)为使用当前字符串的最终长度
* backTrace(arr, i + 1, tmp)为不使用当前字符串的最终长度
*/
return max(len + backTrace(arr, i + 1, bit), backTrace(arr, i + 1, tmp));
}
};