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 16
The 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.
思路:朴素的方法是O(n^2),我们可以在这个基础上进行剪枝。
这个题目有两个技巧:一个是如何最快地判断两个字符串是否含有相同的字符,另一个是如何进行剪枝。
位运算判断相同字符:
对于给的每一个字符串,我们用一个int变量来存储它的字符分布。由于给的字符串只含有26个小写字母,我们只需要26位就能存储下来(若某个字母出现,就将对应的位置为1,否则为0)。当我们需要判断两个字符串是否含有相同的字符时,只需将对应的两个int变量进行与运算,若结果为0则不含有相同字符,否则就是有。
剪枝:
将给的所有字符串按照长度进行排序。
调用stl的sort函数时,我们需要重新定义比较函数。当比较函数在类内部定义时,要定义为静态函数。或者直接定义为全局函数。因此leetcode对静态函数支持不好,所以我用全局函数来实现。
我们先从最后两个字符串进行考虑,因为这两个的长度乘积是所有可能中最大的。
我们用两个指针来指向他们:假设i是最后一个字符串,j是倒数第二个。
我们将j依次向前移动,直到字符串i和j不含有重复字符。
记录下此时两个字符串长度的乘积,用res来表示。然后j不再需要进行前移,因为再往前j所指向的字符串的长度只会变小,不可能获得更大的结果。
此时,唯一可能产生更大结果的情况是:存在两个字符串m和n,它们的下标构成关系j < m < n < i。可以看到,这时m和n的字符串长度都不小于j,因此两者的乘积有可能会大于当前的结果。这里我们用floor来表示j所在的位置。
因此我们将i前移一位,然后j指向i-1的位置继续重复这个步骤。不过这里,j只需要前移到floor+1 (上一次找到结果时j所在位置的下一位)的位置。因为,上一次找到结果时,j所在的位置为floor,这一次如果j指向了floor或者更前的位置,则两个字符串的乘积一定比之前的结果要小,因为j和i指向的字符串的长度都比之前要短了。
1 bool shorter(const string& a, const string& b) 2 { 3 return a.size() < b.size(); 4 } 5 class Solution { 6 public: 7 int maxProduct(vector<string>& words) { 8 sort(words.begin(), words.end(), shorter); 9 vector<int> dict(words.size()); 10 for (int i = 0, n = words.size(); i < n; i++) 11 for (int j = 0, len = words[i].size(); j < len; j++) 12 dict[i] |= (1 << (int)(words[i][j] - ‘a‘)); 13 int floor = -1, res = 0; 14 for (int i = words.size() - 1; i > floor; i--) 15 for (int j = i - 1; j > floor; j--) 16 if (!(dict[i] & dict[j])) 17 { 18 int l1 = words[i].size(), l2 = words[j].size(); 19 res = max(res, l1 * l2); 20 floor = j; 21 break; 22 } 23 return res; 24 } 25 };