1.题目
2.求解
这道题似乎没有我想象的那么难,我用状压dp去求解似乎有点大材小用,效率也不是特别好。我看三叶姐用dfs+剪枝就做出来了,思索,我的脑子里是不是看到啥题都像DP :(
不管怎么说是写出来了,给大家讲讲我的想法,如果有状压dp基础的大伙们(我相信大家都很强)肯定知道用二进制数组表示当前状态,我用数组下标表示我取了哪些字符串,dp数组内仍为一个二进制数,26位的,也就是当前所取的字符串内包含的所有字符。
因为都是字符串操作,因此也就无法使用正常的状态转移方程,换种方式写:
if(!(i & (1 << j))){
int num = to2(arr[j]); //to2 函数是用来将字符串转换成二进制
if(num == -1) continue;
if(!(dp[i] & num)){
dp[i | (1 << j)] = dp[i] | num;
ans = max(ans, __builtin_popcount(dp[i | (1 << j)]));
}
思索没有状态转移方程还能叫动态规划吗?
如果上面这段代码有点无头无尾,那么就上完整代码:
3.代码
int dp[(1 << 16)];
class Solution {
public:
int to2(string str){
int ans = 0;
int num;
for(int i = 0; i < str.size(); i++){
num = 1 << (str[i] - 'a');
if (ans & num) return -1;
ans += num;
}
return ans;
}
int maxLength(vector<string>& arr) {
memset(dp, -1, sizeof(dp));
int i, j;
int len = arr.size();
dp[0] = 0;
int ans = 0;
for(i = 0; i < (1 << len); i++){
if (dp[i] == -1) continue;
for(j = 0; j < len; j++){
if(!(i & (1 << j))){
int num = to2(arr[j]);
if(num == -1) continue;
if(!(dp[i] & num)){
dp[i | (1 << j)] = dp[i] | num;
ans = max(ans, __builtin_popcount(dp[i | (1 << j)]));
}
}
}
}
return ans;
}
};
4.优化
我对选择字符串处进行了一定的优化,字符串数组只会从当前选过得最高位再往后取,实际上就是多了一行对j选值的if语句,看效果是还不错,给大家看看改过后的代码:
int dp[(1 << 16)];
class Solution {
public:
int to2(string str){
int ans = 0;
int num;
for(int i = 0; i < str.size(); i++){
num = 1 << (str[i] - 'a');
if (ans & num) return -1;
ans += num;
}
return ans;
}
int maxLength(vector<string>& arr) {
memset(dp, -1, sizeof(dp));
int i, j;
int len = arr.size();
dp[0] = 0;
int ans = 0;
for(i = 0; i < (1 << len); i++){
if (dp[i] == -1) continue;
if(i == 0) j = 0;
else j = int(log(i) + 1);
for(; j < len; j++){
if(!(i & (1 << j))){
int num = to2(arr[j]);
if(num == -1) continue;
if(!(dp[i] & num)){
dp[i | (1 << j)] = dp[i] | num;
ans = max(ans, __builtin_popcount(dp[i | (1 << j)]));
}
}
}
}
return ans;
}
};