题目一
思路
我们需要在一个一维数组中找到任意两个目标串,进行长度乘积,避免不了是O(N^2)的寻找,我们能做的是,在每次进行比较的时候,保证O(1)内求出答案,并且对搜素过程进行剪枝,比如将之缩减比较次数,或者用一些比如贪心的思想减少比较次数。
模拟法(暴力解法)
直接模拟题意,每次比较先把一个字符串存进set中,遍历另一个串,看有没有重复字符。
二:位运算法
1、根据题目中数据约定,每个字符串最多由26个字母所组成。
2、在两个二进制数中,对应位是不是都为1,用&操作判断。
3、看题意,有用的两个字符串是两个串中没有重复出现的字符,也就是说得去找两个串中出现的字符,关注点在于每个字符出现or没出现,很明显就两个状态,0,1即可表示,所以用位运算即可。一旦一样的字符出现了,对应位置两个1,反之一个1,一个0或者都是零,用&操作就能分得开。
4、所以,再由一共26中字符这个条件,我们就可以得到算法:准备一个长度为26(二进制)的int正数(int最大31位,不越界),从0-25位分别表示a-z,默认都是0,哪位出现了为1,这样就把字符串转换成了一个二进制数,如果没有重复出现的字符,二者按位&结果一定是0.
代码
一
class Solution {
public:
int maxProduct(vector<string>& words) {
int ans = 0;
unordered_set<char> item;
for (int i = 0; i < words.size(); i++) {
for (int k = 0; k < words[i].size(); k++) {
item.insert(words[i][k]);//存第一个字符串出现的字符
}
for (int j = i + 1; j < words.size(); j++) {
bool flag = false;
for (int k = 0; k < words[j].size(); k++) {
if (item.count(words[j][k]) > 0) {//按位比较
flag = true;
break;
}
}
if (flag) {
continue;
}
else {
int tmp = words[i].size()*words[j].size();
ans = max(ans, tmp);
}
}
item.clear();//需要进行清理,把上一个串的信息清了。
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):
二
class Solution {
public:
int maxProduct(vector<string>& words) {//本题的矛盾点在于怎么在O(1)内看出二者也没有相同的字符
//也就是说得去找两个串中出现的字符,关注点在于每个字符出现or没出现,很明显就两个状态,0,1即可表示
//一旦一样的字符出现了,对应位置两个1,反之一个1,一个0或者都是零,用&操作就能分得开。
int ans = 0;
vector<int> marks(words.size(), 0);//存预处理结果
for (int i = 0; i < words.size(); i++) {//将字符串进行一个预处理,转化成一个十进制数
int tar = 0;
for (char c : words[i]) {
int k = c - 'a';//对应的第几位
tar |= (1 << k);//并入tar中
}
marks[i] = tar;
}
for (int i = 0; i < words.size(); i++) {
for (int j = i + 1; j < words.size(); j++) {
if ((marks[i] & marks[j]) == 0) {//这里一定加好括号!!!!!!!
int tmp = words[i].size()*words[j].size();
ans = max(ans, tmp);
}
}
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):
附
其实位运算的算法还有进步空间,因为正如我上面说的,在查找任何两个字符串方面上,有很大进步空间,同样的十进制数,我们这里用贪心的想法进行优化:mark值一样,我要求的是长度之积最大,所以各个分量也要尽可能大,所以mark一样留下长度大的。