1239. 串联字符串的最大长度
给定一个字符串数组 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个小写字母。我们对于给定的字符串数组的每一个字符串都转换成一个这样的二进制整数,接着判断转换成的二进制整数是否满足不含有重复字母的规定,来决定要不要将其装入新构建的flags数组内。
- 装填完毕后,我们只需要将得到的flags,flagsSize,遍历的初始下标0,初始的flag代入DFS函数即可,这里的flag为衡量组成的串联字符串的字母组成情况的二进制整数,开始为0,因为未装入任何字符。
- DFS中,每次向两个方向延伸即可,注意的是,需要满足此时的flag 与遍历到的flags[dir]
没有相同的字母才可以向两个方向延伸,否则只能向一个方向延伸。 - 注意的是这里由于转换成二进制数,我们发现判断是否有重合字母我们使用位运算可轻松解决。
代码:
int result ;
void dfs(int* flags,int flagsSize,int dir,int flag){
if(dir == flagsSize){
result = fmax(result, calOne(flag) );
return;
}
if((flag & flags[dir]) == 0){
dfs(flags , flagsSize , dir+1 , flag | flags[dir]);
}
dfs(flags , flagsSize , dir+1 , flag);
}
int calOne(int flag){ //计算flag二进制形式中1的个数
int One = 1;
int count = 0;
for(int i=0;i<26;i++){
if( ( (flag >> i) & One) == 1){
count++;
}
}
return count;
}
int maxLength(char ** arr, int arrSize){
result = 0; //leetcode中尽量避免使用全局变量,即使使用了也应该在函数内部定义大小
int flags[arrSize];
int flagsSize = 0;
for(int i=0;i<arrSize;i++){
int flag = 0;
int len = strlen(arr[i]);
for(int j=0;j<len;j++){
int temp = arr[i][j]-'a';
if(( ( flag >> temp ) & 1 ) == 1 ){
flag = 0;//作为不添加进数组的标志位
break;
}
//else{
flag = ( flag | (1<<temp) );
//}
}
if(flag > 0){
flags[flagsSize++] = flag;
}
}
dfs(flags,flagsSize,0,0);
return result;
}