Skip to content

Commit 086f35e

Browse files
maximum product of word lengths
1 parent b00d05c commit 086f35e

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package medium;
2+
3+
import java.util.Arrays;
4+
import java.util.Comparator;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
8+
import utils.CommonUtils;
9+
10+
/**318. Maximum Product of Word Lengths QuestionEditorial Solution My Submissions
11+
Total Accepted: 29054
12+
Total Submissions: 71485
13+
Difficulty: Medium
14+
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.
15+
16+
Example 1:
17+
Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"]
18+
Return 16
19+
The two words can be "abcw", "xtfn".
20+
21+
Example 2:
22+
Given ["a", "ab", "abc", "d", "cd", "bcd", "abcd"]
23+
Return 4
24+
The two words can be "ab", "cd".
25+
26+
Example 3:
27+
Given ["a", "aa", "aaa", "aaaa"]
28+
Return 0
29+
No such pair of words.*/
30+
public class MaximumProductOfWordLengths {
31+
//Inspired by this awesome post: https://discuss.leetcode.com/topic/35539/java-easy-version-to-understand
32+
//Idea: this question states that all words consisted of lower case (total only 26 unique chars),
33+
//this is a big hint that we could use integer (total 32 bits) to represent each char
34+
//values[i] means how many unique characters this string words[i] has
35+
public int maxProduct(String[] words){
36+
if(words == null || words.length == 0) return 0;
37+
int len = words.length;
38+
int[] values = new int[len];
39+
for(int i = 0; i < words.length; i++){
40+
String word = words[i];
41+
for(int j = 0; j < words[i].length(); j++){
42+
values[i] |= 1 << (word.charAt(j) -'a');//the reason for left shift by this number "word.charAt(j) -'a'" is for 'a', otherwise 'a' - 'a' will be zero and 'a' will be missed out.
43+
}
44+
}
45+
int maxProduct = 0;
46+
for(int i = 0; i < words.length; i++){
47+
for(int j = 0; j < words.length; j++){
48+
//check if values[i] AND values[j] equals to zero, this means they share NO common chars
49+
if((values[i] & values[j]) == 0 && words[i].length() * words[j].length() > maxProduct) maxProduct = words[i].length()*words[j].length();
50+
}
51+
}
52+
return maxProduct;
53+
}
54+
55+
//This is still failed due to TLE, O(n^3) algorithm is the core defect, you'll have to come up with a faster one!
56+
public int maxProduct_with_pruning(String[] words) {
57+
int maxProduct = 0;
58+
//use a customized comparator to make the words list sorted in descending order, brilliant!
59+
Arrays.sort(words, new Comparator<String>(){
60+
@Override
61+
public int compare(String o1, String o2) {
62+
if(o1.length() > o2.length()) return -1;
63+
else if(o1.length() < o2.length()) return 1;
64+
else return 0;
65+
}
66+
});
67+
for(int i = 0; i < words.length-1; i++){
68+
String currWord = words[i];
69+
int currWordLen = currWord.length();
70+
if(maxProduct > currWordLen * words[i+1].length()) break;//pruning
71+
char[] chars = currWord.toCharArray();
72+
Set<Character> set = new HashSet();
73+
for(char c : chars) set.add(c);
74+
for(int j = i+1; j < words.length; j++){
75+
char[] chars2 = words[j].toCharArray();
76+
boolean valid = true;
77+
for(char c : chars2){
78+
if(set.contains(c)) {
79+
valid = false;
80+
break;
81+
}
82+
}
83+
if(valid){
84+
int thisWordLen = words[j].length();
85+
maxProduct = Math.max(maxProduct, thisWordLen*currWordLen);
86+
}
87+
}
88+
}
89+
return maxProduct;
90+
}
91+
92+
/**My natural idea is an O(n^3) algorithm, I thought of Trie, but I don't think it applies well to this question.
93+
* This following algorithm made it pass 173/174 test cases, as expected, failed by the last extreme test cases due to TLE.*/
94+
public int maxProduct_most_brute_force(String[] words) {
95+
int maxProduct = 0;
96+
for(int i = 0; i < words.length-1; i++){
97+
String currWord = words[i];
98+
int currWordLen = currWord.length();
99+
char[] chars = currWord.toCharArray();
100+
Set<Character> set = new HashSet();
101+
for(char c : chars) set.add(c);
102+
for(int j = i+1; j < words.length; j++){
103+
char[] chars2 = words[j].toCharArray();
104+
boolean valid = true;
105+
for(char c : chars2){
106+
if(set.contains(c)) {
107+
valid = false;
108+
break;
109+
}
110+
}
111+
if(valid){
112+
int thisWordLen = words[j].length();
113+
maxProduct = Math.max(maxProduct, thisWordLen*currWordLen);
114+
}
115+
}
116+
}
117+
return maxProduct;
118+
}
119+
120+
public static void main(String...strings){
121+
MaximumProductOfWordLengths test = new MaximumProductOfWordLengths();
122+
String[] words = new String[]{"abcw","baz","foo","bar","xtfn","abcdef"};
123+
// System.out.println(test.maxProduct_with_pruning(words));
124+
// System.out.println(test.maxProduct(words));
125+
126+
//The following is to understand what does left shift by 1 mean:
127+
//the tricky part is to understand how it's written for me:
128+
// "x << y" means left shift x by y bits
129+
//left shift is equivalent to multiplication of powers of 2, so "4 << 1" equals to " 4 * 2^1"
130+
//similarly, "4 << 3" equals to "4 * 2^3" which equals "4 * 8"
131+
String sample = "f";
132+
int bits = 0, shiftLeftByHowMany = 0, shiftLeftResult = 0;
133+
for(int j = 0; j < sample.length(); j++){
134+
shiftLeftByHowMany = sample.charAt(j) -'a';
135+
shiftLeftResult = 1 << shiftLeftByHowMany;
136+
bits |= 1 << (sample.charAt(j) -'a');//this means shift left 1 by "sample.charAt(j) -'a'" bits
137+
System.out.println("nonShiftLeft = " + shiftLeftByHowMany + "\tnonShiftLeft binary form is: " + Integer.toBinaryString(shiftLeftByHowMany)
138+
+ "\nshiftLeft = " + shiftLeftResult + "\tshiftLeft binary form is: " + Integer.toBinaryString(shiftLeftResult)
139+
+ "\nbits = " + bits + "\tbits binary form is: " + Integer.toBinaryString(bits));
140+
System.out.println(shiftLeftResult == (1 * Math.pow(2, shiftLeftByHowMany)));
141+
}
142+
143+
//similarly, right shift is written like this: "x >> y", means shift x by y bits
144+
//4 >> 3 equals 4 * 2^3, see below:
145+
System.out.println(4*8 == (4 * Math.pow(2, 3)));
146+
}
147+
}

0 commit comments

Comments
 (0)