1. 题目描述
https://leetcode.com/problems/maximum-product-of-word-lengths/#/description
Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.
Example 1:
Given [“abcw”, “baz”, “foo”, “bar”, “xtfn”, “abcdef”]
Return 16The two words can be “abcw”, “xtfn”.
Example 2:
Given [“a”, “ab”, “abc”, “d”, “cd”, “bcd”, “abcd”]
Return 4
The two words can be “ab”, “cd”.Example 3:
Given [“a”, “aa”, “aaa”, “aaaa”]
Return 0
No such pair of words.Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.
Subscribe to see which companies asked this question.
2. 题目分析
其实通过分析题干,我们就已经获取的得到了处理这个题目的基本思路了
for i in words:
for j in words:
if (words[i], words[j] 没有公共部分):
统计最大乘积长度
3. 代码展示
3.1 first-version
按照上面描述的这个思路,我们给出下面代码:
class Solution {
public:
int maxProduct(vector<string>& words) {
size_t wordSize = words.size();
if (wordSize < 2)
return 0;
int maxProductValue = 0;
for (size_t i = 0; i < wordSize; i++){
for (size_t j = i + 1; j < wordSize; j++){
if (!hasCommonElement(words[i], words[j])){
int productVelue = words[i].length() * words[j].length();
if (maxProductValue < productVelue){
maxProductValue = productVelue;
}
}
}
}
return maxProductValue;
}
private:
bool hasCommonElement(const string & a, const string & b) const{
auto bitseta = convertStringToBitset(a);
auto bitsetb = convertStringToBitset(b);
auto bitsetCommon = (bitseta & bitsetb);
return bitsetCommon.any();
}
bitset<26> convertStringToBitset(const string & str) const{
bitset<26> stringa;
for (size_t i = 0; i < str.length(); i++){
if (!stringa.test(str.at(i) - 'a'))
stringa.set(str.at(i) - 'a');
}
return stringa;
}
};
运行结果:
3.2 second-version
这个运行效率,感觉不能忍受啊,怎么这么慢,通过分析,我们发现,程序在hasCommentElement这个子函数中的调用非常耗时, 通过分析代码,我们可以知道,实际上对每个word只要执行一次, ie,
O(n)
的 convertStringToBitset被执行了
O(n2)
量级次,因而由此表现出来的效果就体现在了这个时间效率上
于是,我们可以通过把这部分公共转换提取到外围去得到如下代码:
class Solution {
public:
int maxProduct(vector<string>& words) {
size_t wordSize = words.size();
if (wordSize < 2)
return 0;
vector<bitset<26>> bitsetArray;
for (size_t i = 0; i < wordSize; i++){
bitsetArray.emplace_back(convertStringToBitset(words[i]));
}
int maxProductValue = 0;
for (size_t i = 0; i < wordSize; i++){
for (size_t j = i + 1; j < wordSize; j++){
if (!hasCommonElement(bitsetArray[i], bitsetArray[j])){
int productVelue = words[i].length() * words[j].length();
if (maxProductValue < productVelue){
maxProductValue = productVelue;
}
}
}
}
return maxProductValue;
}
private:
inline bool hasCommonElement(const bitset<26> & bitseta, const bitset<26> & bitsetb) const{
auto bitsetCommon = (bitseta & bitsetb);
return bitsetCommon.any();
}
bitset<26> convertStringToBitset(const string & str) const{
bitset<26> stringa;
for (size_t i = 0; i < str.length(); i++){
if (!stringa.test(str.at(i) - 'a'))
stringa.set(str.at(i) - 'a');
}
return stringa;
}
};
此时代码执行效果为:
运行效率立马得到了显著的提升,然而,本质上我们这个算法的时间复杂度还是处于
O(n2)
量级的
4. 参考解法
https://discuss.leetcode.com/topic/31766/bit-shorter-c
这个解法的基本思想是使用hash表,将代码的时间复杂度控制在
O(n)
的量级, 链接中的讨论主要在纠结为什么这个复杂度是
O(n)
而不是
O(n2)
, 这是因为我们所构建的hash表的第一项本质上是一个 26bit 长度的位图,在最差情况下,hash表的长度只有
226
, 这个数字虽然很大,但是根据他实际上只是以个很大的常数,因而算法复杂度还是在
O(n)
的量级。
int maxProduct(vector<string>& words) {
vector<int> mask(words.size());
int result = 0;
for (int i=0; i<words.size(); ++i) {
for (char c : words[i])
mask[i] |= 1 << (c - 'a');
for (int j=0; j<i; ++j)
if (!(mask[i] & mask[j]))
result = max(result, int(words[i].size() * words[j].size()));
}
return result;
}