典型例题:最长无重复字符的串联字符串
题目解析
此题看完之后,字符相关且不能含重复字符,便能想到用位运算判重,而其中要求的是数组子序列串相连,这就很明显牵扯到组合问题,组合问题,我们可用多种方式枚举组合,可用二进制枚举组合,也可用dfs枚举,而dfs枚举也有两种形式:偏二进制的形式||回溯形式。
这题由于最后要求返回一个最长的 无重复字符的字符串,则该字符串长度肯定不会超过26(毕竟也就26个小写字母)所以完全可以通过操控各个字符串的二进制序列来得到答案。
两种形式的dfs解决
dfs回溯形式
class Solution {
public:
int maxLength(vector<string>& arr) {
//先去掉arr中有重复字符的字符串,由于字符的范围是小写字母--故ASCII差值不会超过26,故可用每一位计数用于判重
vector<int>newArr;
//由于此题最后的最大长度字符串不含重复字符,所以完全可用二进制位代替字符串来进行判重等迅速计算
//建合要求二进制表
for(auto &s:arr){
int check = 0;
for(auto &ch:s){
int dist = ch - 'a';
//一旦出现重复字符则直接跳出,否则继续建表
if(check&(1<<dist))
goto end;
check |= 1<<dist;
}
newArr.push_back(check);
end:{}
}
size = newArr.size();
dfs(newArr,0,0);
return res;
}
private:
int size;
int res = 0;
int countSize(int t){
int count = 0;
while(t){
t = t&(t-1);
count++;
}
return count;
}
void dfs(vector<int>&arr,int x,int pos){
//由于不确定取几个,每层都把res进行更新
res = max(res,countSize(x));
int temp = x;
//temp回溯,让每一层只负责选择其中一个
for(int i=pos;i<size;i++){
if(x&arr[i])
continue;
x |= arr[i];
dfs(arr,x,i+1);
x = temp;
}
}
};
dfs二进制枚举形式
class Solution {
public:
int maxLength(vector<string>& arr) {
//先去掉arr中有重复字符的字符串,由于字符的范围是小写字母--故ASCII差值不会超过26,故可用每一位计数用于判重
vector<int>newArr;
//由于此题最后的最大长度字符串不含重复字符,所以完全可用二进制位代替字符串来进行判重等迅速计算
//建合要求二进制表
for(auto &s:arr){
int check = 0;
for(auto &ch:s){
int dist = ch - 'a';
//一旦出现重复字符则直接跳出,否则继续建表
if(check&(1<<dist))
goto end;
check |= 1<<dist;
}
newArr.push_back(check);
end:{}
}
size = newArr.size();
dfs(newArr,0,0);
return res;
}
private:
int size;
int res = 0;
int countSize(int t){
int count = 0;
while(t){
t = t&(t-1);
count++;
}
return count;
}
//取与不取相当于1和0,所以很像二进制枚举
void dfs(vector<int>&arr,int x,int pos){
//相当于取完了这一次的所有位置,即相当于取完了一个二进制串如01011
if(pos==size){
res = max(res,countSize(x));
return;
}
//取该位元素
if(!(x&arr[pos]))
dfs(arr,x|arr[pos],pos+1);
//不取该位元素,到下一位
dfs(arr,x,pos+1);
}
};