一、Problem
Given an array of strings arr. String s is a concatenation of a sub-sequence of arr which have unique characters.
Return the maximum possible length of s.
Input: arr = ["un","iq","ue"]
Output: 4
Explanation: All possible concatenations are "","un","iq","ue","uniq" and "ique".
Maximum length is 4.
Constraints:
1 <= arr.length <= 16
1 <= arr[i].length <= 26
arr[i] contains only lower case English letters.
二、Solution
方法一:錯誤解法
錯誤解法:WA 74/83
class Solution {
public int maxLength(List<String> sl) {
int max = 0;
l1:
for (int i = 0; i < sl.size(); i++) {
Set<Character> st = new HashSet<>();
StringBuilder sb = new StringBuilder(sl.get(i));
for (char c : sl.get(i).toCharArray()) if (st.add(c) == false) {
if (sl.size() == 1)
return 0;
continue l1;
}
l2:
for (int j = i+1; j < sl.size(); j++) {
for (char c : sl.get(j).toCharArray()) if (st.add(c) == false) {
continue l2;
}
sb.append(sl.get(j)); // 錯
}
if (max < sb.length())
max = sb.length();
}
return max;
}
}
["a", "abc", "d", "de", "def"]
輸出:4
預期:6
想的有點簡單了,用 sb append 掉一個字符串 s 後,如果下一個字符串 s1 比該還要長,但是卻有相同字符,這就錯了。所以這涉及到選 s1 與不選 s1
方法二:二進制枚舉
1 <= arr[i].length <= 26
,告訴我們用 32 位二進制即可表示每一個字符串的選或否.
老毛病: 經常性地把 i & (1 << j)
、mask & (1 << shift)
的結果看成是 0/1
,比賽時改了好久,其實這個結果表示的某一項選沒選而已,所以,但 i 和 mask 的其它位置還是有意義的。
class Solution {
int mask;
boolean isUnique(String s) {
for (char c : s.toCharArray()) {
int shift = c - 'a';
if ((mask & (1 << shift)) > 0)
return false;
mask |= (1 << shift);
}
return true;
}
public int maxLength(List<String> sl) {
int n = sl.size(), tot = 1 << n, max = 0;
for (int i = 0; i < tot; i++) {
int cur = 0;
mask = 0;
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) > 0 && isUnique(sl.get(j)))
cur += sl.get(j).length();
}
if (cur > max) max = cur;
}
return max;
}
}
複雜度分析
- 時間複雜度:,最壞情況是,每一個字符串都選
- 空間複雜度:,
方法三:回溯
思想和 二級制枚舉是一樣的,只不過,遞歸幫我們把每一種合法情況都給隱式地枚舉出來了,而不是手寫 for (i < tot)
,但不清楚爲什麼,遞歸比較快。。。
class Solution {
int N, max;
StringBuilder sb;
List<String> sl;
void dfs(int i, int mask) {
if (i == N) {
if (sb.length() > max)
max = sb.length();
return;
}
int m = mask;
boolean fl = true;
String s = sl.get(i);
for (char c : s.toCharArray()) {
int shift = c - 'a';
if ((m & (1 << shift)) > 0) {
fl = false;
break;
}
m |= (1 << shift);
}
if (fl) {
sb.append(s);
dfs(i+1, m);
sb.setLength(sb.length()-s.length());
}
dfs(i+1, mask);
}
public int maxLength(List<String> sl) {
N = sl.size();
this.sl = sl;
sb = new StringBuilder();
dfs(0, 0);
return max;
}
}