Leetcode - 串联字符串的最大长度

192 阅读1分钟

这是我参与更文挑战的第19天,活动详情查看更文挑战

题目描述

给定一个字符串数组 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] 中只含有小写英文字母

解题思路

需要计算可行解的长度,至于可行解具体是什么,以及可行解中各个字符的顺序我们是不用考虑的。因此构成可行解的每个字符串均可以视作一个字符集合,且集合不含重复元素。

由于构成可行解的字符串仅包含小写字母,且无重复元素,我们可以用一个二进制数来表示该字符串的字符集合,二进制的第 i 位为 1 表示字符集合中含有第 i 个小写字母,为 0 表示字符集合中不含有第 i 个小写字母。

由于包含重复字母的字符串无法参与构成可行解,因此遍历 arr,从中筛选出无重复字母的字符串,将其对应二进制数加入一数组,记作 masks

接下来,使用回溯法来解决本问题:

我们用 backtrack(pos,mask) 表示递归的函数,其中 pos 表示我们当前递归到了数组 masks 中的第 pos 个数,mask 表示当前连接得到的字符串对应二进制数为 mask

对于第 pos 个数,我们有两种方法:选或者不选。如果 maskmasks 无公共元素,则可以选这个数,此时我们调用 backtrack(pos+1,mask ∣ masks[pos]) 进行递归。如果我们不选这个数,那么我们调用 backtrack(pos+1,mask) 进行递归。

masks 的长度为 n,当 pos=n 时,计算 mask1 的个数,即为可行解的长度,用其更新可行解的最长长度。

代码

C++代码 ``c++ class Solution { public: int maxLength(vector &arr) { vector masks; for (string &s : arr) { int mask = 0; for (char ch : s) { ch -= 'a'; if ((mask >> ch) & 1) { // 若 mask 已有 ch,则说明 s 含有重复字母,无法构成可行解 mask = 0; break; } mask |= 1 << ch; // 将 ch 加入 mask 中 } if (mask > 0) { masks.push_back(mask); } }

    int ans = 0;
    function<void(int, int)> backtrack = [&](int pos, int mask) {
        if (pos == masks.size()) {
            ans = max(ans, __builtin_popcount(mask));
            return;
        }
        if ((mask & masks[pos]) == 0) { // mask 和 masks[pos] 无公共元素
            backtrack(pos + 1, mask | masks[pos]);
        }
        backtrack(pos + 1, mask);
    };
    backtrack(0, 0);
    return ans;
}

};