给定一个字符串数组 arr,字符串 s 是将 arr 的含有 不同字母  的  子序列  字符串 连接  所得的字符串。
请返回所有可行解 s 中最长长度。
子序列  是一种可以从另一个数组派生而来的数组,通过删除某些元素或不删除元素而不改变其余元素的顺序。
示例 1: 
**输入:** arr = ["un","iq","ue"]
**输出:** 4
**解释:** 所有可能的串联组合是:
- ""
- "un"
- "iq"
- "ue"
- "uniq" ("un" + "iq")
- "ique" ("iq" + "ue")
最大长度为 4。
示例 2: 
**输入:** arr = ["cha","r","act","ers"]
**输出:** 6
**解释:** 可能的解答有 "chaers" 和 "acters"。
示例 3: 
**输入:** arr = ["abcdefghijklmnopqrstuvwxyz"]
**输出:** 26
提示: 
1 <= arr.length <= 161 <= arr[i].length <= 26arr[i] 中只含有小写英文字母 
方法一:回溯 + 位运算 我们需要计算可行解的长度,至于可行解具体是什么,以及可行解中各个字符的顺序我们是不用考虑的。因此构成可行解的每个字符串均可以视作一个字符集合,且集合不含重复元素。
由于构成可行解的字符串仅包含小写字母,且无重复元素,我们可以用一个二进制数来表示该字符串的字符集合,二进制的第 i 位为 1 表示字符集合中含有第 i 个小写字母,为 0 表示字符集合中不含有第 i 个小写字母。
由于包含重复字母的字符串无法参与构成可行解,因此遍历 arr,从中筛选出无重复字母的字符串,将其对应二进制数加入一数组,记作 masks。
接下来,使用回溯法来解决本问题:
我们用 backtrack}(\textit{pos}, \textit{mask}) 表示递归的函数,其中 pos 表示我们当前递归到了数组 masks 中的第 pos 个数,mask 表示当前连接得到的字符串对应二进制数为 mask;
对于第 pos 个数,我们有两种方法:选或者不选。如果 mask 和 masks}[\textit{pos}] 无公共元素,则可以选这个数,此时我们调用 backtrack}(\textit{pos}+1, \textit{mask}\ |\ \textit{masks}[\textit{pos}]) 进行递归。如果我们不选这个数,那么我们调用 backtrack}(\textit{pos}+1, \textit{mask}) 进行递归。
记 masks 的长度为 n,当 pos}=n 时,计算 mask 中 1 的个数,即为可行解的长度,用其更新可行解的最长长度。
 
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class  Solution  {public :    int  maxLength (vector<string> &arr)           vector<int > masks;         for  (string &s : arr) {             int  mask = 0 ;             for  (char  ch : s) {                 ch -= 'a' ;                 if  ((mask >> ch) & 1 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             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 ) {                  backtrack (pos + 1 , mask | masks[pos]);             }             backtrack (pos + 1 , mask);         };         backtrack (0 , 0 );         return  ans;     } }; 
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class  Solution  {    int  ans  =  0 ;     public  int  maxLength (List<String> arr)  {         List<Integer> masks = new  ArrayList <Integer>();         for  (String s : arr) {             int  mask  =  0 ;             for  (int  i  =  0 ; i < s.length(); ++i) {                 int  ch  =  s.charAt(i) - 'a' ;                 if  (((mask >> ch) & 1 ) != 0 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             if  (mask > 0 ) {                 masks.add(mask);             }         }         backtrack(masks, 0 , 0 );         return  ans;     }     public  void  backtrack (List<Integer> masks, int  pos, int  mask)  {         if  (pos == masks.size()) {             ans = Math.max(ans, Integer.bitCount(mask));             return ;         }         if  ((mask & masks.get(pos)) == 0 ) {              backtrack(masks, pos + 1 , mask | masks.get(pos));         }         backtrack(masks, pos + 1 , mask);     } } 
[sol1-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public  class  Solution  {    int  ans = 0 ;     public  int  MaxLength (IList<string > arr )         IList<int > masks = new  List<int >();         foreach  (string  s in  arr) {             int  mask = 0 ;             foreach  (char  c in  s) {                 int  ch = c - 'a' ;                 if  (((mask >> ch) & 1 ) != 0 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             if  (mask > 0 ) {                 masks.Add(mask);             }         }         Backtrack(masks, 0 , 0 );         return  ans;     }     public  void  Backtrack (IList<int > masks, int  pos, int  mask )         if  (pos == masks.Count) {             ans = Math.Max(ans, BitCount(mask));             return ;         }         if  ((mask & masks[pos]) == 0 ) {              Backtrack(masks, pos + 1 , mask | masks[pos]);         }         Backtrack(masks, pos + 1 , mask);     }     private  static  int  BitCount (int  i         i = i - ((i >> 1 ) & 0x55555555 );         i = (i & 0x33333333 ) + ((i >> 2 ) & 0x33333333 );         i = (i + (i >> 4 )) & 0x0f0f0f0f ;         i = i + (i >> 8 );         i = i + (i >> 16 );         return  i & 0x3f ;     } } 
[sol1-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 func  maxLength (arr []string ) int ) {    masks := []int {} outer:     for  _, s := range  arr {         mask := 0          for  _, ch := range  s {             ch -= 'a'              if  mask>>ch&1  > 0  {                  continue  outer             }             mask |= 1  << ch          }         masks = append (masks, mask)     }     var  backtrack func (int , int )      backtrack = func (pos, mask int )          if  pos == len (masks) {             ans = max(ans, bits.OnesCount(uint (mask)))             return          }         if  mask&masks[pos] == 0  {              backtrack(pos+1 , mask|masks[pos])         }         backtrack(pos+1 , mask)     }     backtrack(0 , 0 )     return  } func  max (a, b int ) int  {    if  a > b {         return  a     }     return  b } 
[sol1-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 int  ans;void  backtrack (int * masks, int  masksSize, int  pos, int  mask)  {    if  (pos == masksSize) {         ans = fmax(ans, __builtin_popcount(mask));         return ;     }     if  ((mask & masks[pos]) == 0 ) {           backtrack(masks, masksSize, pos + 1 , mask | masks[pos]);     }     backtrack(masks, masksSize, pos + 1 , mask); } int  maxLength (char ** arr, int  arrSize)  {    ans = 0 ;     int  masks[arrSize];     int  masksSize = 0 ;     for  (int  i = 0 ; i < arrSize; ++i) {         int  mask = 0 ;         int  len = strlen (arr[i]);         for  (int  j = 0 ; j < len; ++j) {             int  ch = arr[i][j] - 'a' ;             if  (((mask >> ch) & 1 ) != 0 ) {                   mask = 0 ;                 break ;             }             mask |= 1  << ch;           }         if  (mask > 0 ) {             masks[masksSize++] = mask;         }     }     backtrack(masks, masksSize, 0 , 0 );     return  ans; } 
[sol1-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var  maxLength = function (arr ) {    let  ans = 0 ;     const  masks = [];     for  (const  s of  arr) {         let  mask = 0 ;         for  (let  i = 0 ; i < s.length ; ++i) {             const  ch = s[i].charCodeAt () - 'a' .charCodeAt ();             if  (((mask >> ch) & 1 ) !== 0 ) {                  mask = 0 ;                 break ;             }             mask |= 1  << ch;          }         if  (mask > 0 ) {             masks.push (mask);         }     }     const  backtrack  = (masks, pos, mask ) => {         if  (pos === masks.length ) {             ans = Math .max (ans, mask.toString (2 ).split ('0' ).join ('' ).length );             return ;         }         if  ((mask & masks[pos]) === 0 ) {              backtrack (masks, pos + 1 , mask | masks[pos]);         }         backtrack (masks, pos + 1 , mask);     }     backtrack (masks, 0 , 0 );     return  ans; }; 
[sol1-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class  Solution :    def  maxLength (self, arr: List [str ] ) -> int :         masks = list ()         for  s in  arr:             mask = 0              for  ch in  s:                 idx = ord (ch) - ord ("a" )                 if  ((mask >> idx) & 1 ):                        mask = 0                      break                  mask |= 1  << idx                if  mask > 0 :                 masks.append(mask)         ans = 0          def  backtrack (pos: int , mask: int  ) -> None :             if  pos == len (masks):                 nonlocal  ans                 ans = max (ans, bin (mask).count("1" ))                 return                           if  (mask & masks[pos]) == 0 :                    backtrack(pos + 1 , mask | masks[pos])             backtrack(pos + 1 , mask)                  backtrack(0 , 0 )         return  ans 
复杂度分析 
方法二:迭代 + 位运算 我们可以遍历 arr,维护前 i 个字符串构成的可行解集合,记作 masks。初始时,可行解集合仅包含一个空字符串。
若 arr}[i+1] 中无重复字符,则将其与 masks 中的字符串连接,若连接后仍无重复字符,则将连接后的新字符串加入到 masks 中,这样我们就得到了前 i+1 个字符串构成的可行解集合。
遍历结束后,masks 即为 arr 构成的所有可行解,求出其中最长的可行解,即为答案。
代码实现时,我们沿用方法一,用二进制数表示字符串,并在得到新字符串的同时更新可行解长度最大值。
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class  Solution  {public :    int  maxLength (vector<string> &arr)           int  ans = 0 ;         vector<int > masks = {0 };         for  (string &s : arr) {             int  mask = 0 ;             for  (char  ch : s) {                 ch -= 'a' ;                 if  ((mask >> ch) & 1 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             if  (mask == 0 ) {                 continue ;             }             int  n = masks.size ();             for  (int  i = 0 ; i < n; ++i) {                 int  m = masks[i];                 if  ((m & mask) == 0 ) {                      masks.push_back (m | mask);                     ans = max (ans, __builtin_popcount(m | mask));                 }             }         }         return  ans;     } }; 
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class  Solution  {    public  int  maxLength (List<String> arr)  {         int  ans  =  0 ;         List<Integer> masks = new  ArrayList <Integer>();         masks.add(0 );         for  (String s : arr) {             int  mask  =  0 ;             for  (int  i  =  0 ; i < s.length(); ++i) {                 int  ch  =  s.charAt(i) - 'a' ;                 if  (((mask >> ch) & 1 ) != 0 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             if  (mask == 0 ) {                 continue ;             }             int  n  =  masks.size();             for  (int  i  =  0 ; i < n; ++i) {                 int  m  =  masks.get(i);                 if  ((m & mask) == 0 ) {                      masks.add(m | mask);                     ans = Math.max(ans, Integer.bitCount(m | mask));                 }             }         }         return  ans;     } } 
[sol2-C#] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public  class  Solution  {    public  int  MaxLength (IList<string > arr )         int  ans = 0 ;         IList<int > masks = new  List<int >();         masks.Add(0 );         foreach  (string  s in  arr) {             int  mask = 0 ;             foreach  (char  c in  s) {                 int  ch = c - 'a' ;                 if  (((mask >> ch) & 1 ) != 0 ) {                      mask = 0 ;                     break ;                 }                 mask |= 1  << ch;              }             if  (mask == 0 ) {                 continue ;             }             int  n = masks.Count;             for  (int  i = 0 ; i < n; ++i) {                 int  m = masks[i];                 if  ((m & mask) == 0 ) {                      masks.Add(m | mask);                     ans = Math.Max(ans, BitCount(m | mask));                 }             }         }         return  ans;     }     private  static  int  BitCount (int  i         i = i - ((i >> 1 ) & 0x55555555 );         i = (i & 0x33333333 ) + ((i >> 2 ) & 0x33333333 );         i = (i + (i >> 4 )) & 0x0f0f0f0f ;         i = i + (i >> 8 );         i = i + (i >> 16 );         return  i & 0x3f ;     } } 
[sol2-Golang] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func  maxLength (arr []string ) int ) {    masks := []int {0 }  outer:     for  _, s := range  arr {         mask := 0          for  _, ch := range  s {             ch -= 'a'              if  mask>>ch&1  > 0  {                  continue  outer             }             mask |= 1  << ch          }         for  _, m := range  masks {             if  m&mask == 0  {                  masks = append (masks, m|mask)                 ans = max(ans, bits.OnesCount(uint (m|mask)))             }         }     }     return  } func  max (a, b int ) int  {    if  a > b {         return  a     }     return  b } 
[sol2-C] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 int  maxLength (char ** arr, int  arrSize)  {    int  ans = 0 ;     int  masks[1  << arrSize], masksSize = 0 ;     masks[masksSize++] = 0 ;     for  (int  i = 0 ; i < arrSize; ++i) {         int  mask = 0 ;         int  len = strlen (arr[i]);         for  (int  j = 0 ; j < len; ++j) {             char  ch = arr[i][j] - 'a' ;             if  ((mask >> ch) & 1 ) {                   mask = 0 ;                 break ;             }             mask |= 1  << ch;           }         if  (mask == 0 ) {             continue ;         }         int  n = masksSize;         for  (int  j = 0 ; j < n; ++j) {             int  m = masks[j];             if  ((m & mask) == 0 ) {                   masks[masksSize++] = m | mask;                 ans = fmax(ans, __builtin_popcount(m | mask));             }         }     }     return  ans; } 
[sol2-JavaScript] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 var  maxLength = function (arr ) {    let  ans = 0 ;     const  masks = [0 ];     for  (const  s of  arr) {         let  mask = 0 ;         for  (let  i = 0 ; i < s.length ; ++i) {             const  ch = s[i].charCodeAt () - 'a' .charCodeAt ();             if  (((mask >> ch) & 1 ) !== 0 ) {                  mask = 0 ;                 break ;             }             mask |= 1  << ch;          }         if  (mask === 0 ) {             continue ;         }         const  n = masks.length ;         for  (let  i = 0 ; i < n; ++i) {             const  m = masks[i];             if  ((m & mask) === 0 ) {                  masks.push (m | mask);                 ans = Math .max (ans, (m | mask).toString (2 ).split ('0' ).join ('' ).length );             }         }     }     return  ans; }; 
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  Solution :    def  maxLength (self, arr: List [str ] ) -> int :         ans = 0          masks = [0 ]         for  s in  arr:             mask = 0              for  ch in  s:                 idx = ord (ch) - ord ("a" )                 if  ((mask >> idx) & 1 ):                        mask = 0                      break                  mask |= 1  << idx                if  mask == 0 :                 continue                           n = len (masks)             for  i in  range (n):                 m = masks[i]                 if  (m & mask) == 0 :                        masks.append(m | mask)                     ans = max (ans, bin (m | mask).count("1" ))                  return  ans 
复杂度分析 
时间复杂度:O(|\Sigma|+2^n)。其中 |\Sigma| 是 arr 中所有字符串的长度之和,n 是 arr 的长度。遍历所有字符串需要 O(|\Sigma|),每次循环会将 masks 的大小增加至多一倍,因此总的时间复杂度为 O(|\Sigma|+2^0+2^1+…+2^{n-1})=O(|\Sigma|+2^n)。
空间复杂度:O(2^n)。由于最坏情况下 arr 的所有子集都能构成可行解,这有 2^n 个,这种情况下遍历结束后的 masks 的长度为 2^n,因此空间复杂度为 O(2^n)。