写在前面:题目来源于剑指offer,有些题解也是offer中的,所以想看的更加详细,请直接在LeetCode上搜索
问题一:单词长度的最大乘积
题目:给定一个字符串数组 words,请计算当两个字符串 words[i] 和 words[j] 不包含相同字符时,它们长度的乘积的最大值。假设字符串中只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回 0。
题目拆解:
1)找到两个不包含相同字符的字符串
2)计算字符串长度的乘积
初始思路:
四层for循环,遍历所有结果
int ans = 0;
for(int i =0;i<words.size() - 1;++i)
{
for (int j = i + 1; j < words.size(); ++j)
{
bool sign = true;
//判断 两个字符是否存在相等字母
int w1 = words[i].size(), w2 = words[j].size();
int nsize = min(w1, w2);
for (int k = 0; k < w1; ++k)
{
for(int m = 0; m < w2; ++m)
{
if (words[i][k] == words[j][m])
{
sign = false;
break;
}
}
if(!sign)
{
break;
}
}
int temp = w1 * w2;
if (sign && ans < temp)
ans = temp;
}
}
return ans;
测试结果:超出时间限制。
优化思路:
1)减少比较时间。测试用例中,超出时间的原因是,每个字符串都是相同字符,而且特别长。同时从题目中也可以发现,字符串的输入范围是26个小写字母,可以借助位运算简化比较过程。
示例“abc” = 0000...0111;“abbc” = 0000...0111;
一开始想半天不知道如何用位运算表达,看了题解才发现如此简单,学无止境。
int sumlen = words.size();
int i =0,j=0;
vector<int> maskdata(sumlen,0);
for(i = 0; i<sumlen;++i)
{
for(j = 0; j<words[i].size(); ++j)
{
maskdata[i] |= (1<<(words[i][j] - 'a'));
}
}
int ans = 0;
for(i =0;i<words.size() - 1;++i)
{
for (j = i + 1; j < words.size(); ++j)
{
int w1 = words[i].size(), w2 = words[j].size();
int temp = w1 * w2;
// 比当前答案大才有计算的必要
if(temp < ans)
continue;
// 判断是否存在相同字符
if(maskdata[i] & maskdata[j])
continue;
// 更新答案
ans = temp;
}
}
return ans;
上述代码是看过题解之后自己编写的,算是一点点自己的东西,哈哈,又是无效学习的一天。
此时我已经想不到优化的方向了,所以继续看题解:
题解中的优化是考虑到了mask重复的问题,进行减少计算,但是大方向并没有改变,因此并不是另一种思路。
启示:位运算也可以做去重复的操作。
题目二:排序数组中两个数字之和
给定一个已按照 升序排列 的整数数组 numbers
,请你从数组中找出两个数满足相加之和等于目标数 target
解题思路:
问题定性为查找问题,而且已知数组为升序数组,所以目标应该进一步细化为减少搜索任务上,因此,我的第一个思路为遍历数组,有序从数组中取值与target作差,在数组中通过二分法查找差值。
int first = 0;
int nsize = numbers.size();
for(first = 0; first < nsize; ++first)
{
int ohalf = target - numbers[first];
int bl = first +1;
int br = nsize - 1;
while(bl<=br)
{
int mid = bl + ( (br - bl) >> 1);
if(numbers[mid] == ohalf)
return {first, mid};
else if(numbers[mid] > ohalf)
br = mid - 1;
else
bl = mid + 1;
}
}
return {0};
编写过程中自己的问题是,二分查找的判断条件,左边边界是小于等于还是小于,如果是小于,则跳出while循环时还需要讨论左边是否等于右边界的情况。
还有一个思维误区是判断是否加1的问题,在选择小于的情况下,如何解决3和4反复横跳的问题。一直陷在思维误区,因此以后遇到二分查找问题,只需要考虑数据即可
查找模式
int bl = 查找起始位置
int br = 查找结束位置
while(bl <= br)
{
int mid = bl + ( (br - bl) >> 1);
if(等于中间值)
else if(大于中间值)
mid + 1
else 小于中间值
mid - 1
}
现在做题做的比较少,先这样考虑,遇到新问题再回来修改(2022.8.1)
昨天临走,又发现双指针方法也适合有序数组的查找
int bl = 0;
int br = numbers.size() - 1;
while(bl < br)
{
if(numbers[bl] + numbers[br] == target)
return {bl,br};
else if(numbers[bl]+numbers[br]>target)
br -=1;
else
bl +=1;
}
return {0};
总结:
二分查找、双指针、位运算去重