面试题5:单词长度的最大乘积
力扣链接:https://leetcode-cn.com/problems/aseY1I/
题目描述
给定一个字符串数组words,请计算当两个字符串words[i]和words[j]不包含相同字符时,它们的长度的乘积的最大值。假设字符串只包含英语的小写字母。如果没有不包含相同字符的一对字符串,返回0。
样例输入1
words = [“abcw”,“baz”,foo",“bar”,“fxyz”,“abcdef”]
样例输出1
16
解释:“abcw"和"fxyz”
样例输入2
words = [“a”,“ab”,“abc”,“d”,“cd”,“bcd”,“abcd”]
样例输出2
4
解释:“ab"和"cd”
样例输入3
words = [“a”,“aa”,“aaa”,“aaaa”]
样例输出3
0
解析
解决这个问题的关键在于如何判断两个字符串str1和str2中没有相同的字符。一个直观的想法就是基于字符串str1中的每个字符ch,扫描字符串str2判断字符ch是否出现在str2中。如果两个字符串的长度分别为p和q,那么这种蛮力法的时间复杂度为O(pq)。显然是不可取的,我们可以用哈希表进行优化。
哈希表记录字符串中出现的字符
对于每个字符串,可以用一个哈希表记录出现在该字符串中的所有字符。在判断两个字符串str1和str2中是否有相同的字符时,只需要从’a’到’z’判断某个字符串是否在两个字符串对应的哈希表中都出现了。利用哈希表查找的速度是O(1)。这个题目假设所有字符都是英文小写字母,只有26个可能的字符,因此最多只需要在每个字符串对应的哈希表中查询26次就能判断两个字符串是否包含相同的字符。时间复杂度是O(1)。
class Solution {
public:
int maxProduct(vector<string>& words) {
int n=words.size();
vector<vector<bool>> vis(n,vector<bool>(26,false));
//预处理
for(int i=0;i<n;i++){
int m=words[i].size();
for(int j=0;j<m;j++) vis[i][words[i][j]-'a']=true;
}
int ans=0;
//开始循环
for(int i=0;i<n;i++){
int a=words[i].size();
for(int j=i+1;j<n;j++){
int b=words[j].size();
int flag=0;
for(int k=0;k<26;k++){
if(vis[i][k]&&vis[j][k]) {flag=1;break;}
}
if(flag==0) ans=max(ans,a*b);
}
}
return ans;
}
};
上述代码分为两部分。第一步,初始化每个字符串对应的哈希表。时间复杂度是O(nk)其中n表示字符串的规模,k表示每个字符串长度的规模。第二步,根据哈希表找到不含相同字符的两个字符串的最大乘积,时间复杂度是O(n2)。总的时间复杂度就是O(nk+n2)。
用整数的二进制数位记录字符串中出现的字符
继续优化:既然每个字符串中的字符都是小写字符,不重复的字符最多有26个,而一个整数能够存储32个比特位,于是乎可以使用二进制记录字符串中出现的每个字符。出现的字符i就在二进制从右往左数第(i-‘a’)个数位上显示1否则显示0。如果两个字符串中不包含相同的字符,那么这两个字符串按位与&的结果一定是0,可以根据这个判断两个字符串是否含有相同的字符。
class Solution {
public:
int maxProduct(vector<string>& words) {
int n=words.size();
vector<int> flag(n,0);
for(int i=0;i<n;i++){
for(char c:words[i]) flag[i] |= 1<<(c-'a');
}
int ans=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if((flag[i]&flag[j])==0) {
int te=words[i].size()*words[j].size();
ans=max(ans,te);
}
}
}
return ans;
}
};