原问题:
给定一个字符串数组,找到长度的最大值length(word[i]) * length(word[j])
,个中两个单词中的字母无雷同。您能够假定每一个单词只包括小写字母。假如没有这两个词,返回0。
例:
Input: ["abcw","baz","foo","bar","xtfn","abcdef"]
Output: 16
Explanation: The two words can be "abcw", "xtfn".
剖析:
这题肯定要举行交织对照(2个for轮回),但最症结的就是对照历程,也就是推断2个字符串是不是存在雷同的字符。
假如运用indexOf
或许数组下标纪录都邑形成时刻复杂度大幅提拔,看了别人的答案发明运用的是位操作符<<
,|
和&
,而且是在交织对照之前举行预处理,交织对照的时刻只需要简朴的推断pretreate[i] & pretreate[j]===0
便可,
由于运用后效力提拔太多,剖析而且纪录一下。
先诠释val |= (1 << (word.charCodeAt(i)-aCode))
:
-
word.charCodeAt(i)-aCode
这个很好懂,也就是a对应0,b对应1…这里的0,1数字代表的是
二进制1背面的位数。 -
1<<0
,1<<1
是什么呢?1在二进制中(32位)就是
00000000000000000000000000000001
,<<
是左移1位,那末
1<<0
照样1
,1<<1
就是(前面的零省略)10
,1<<2
就是100
,1<<3
就是1000
,因而可知
a
就是1
,b
是10
,c
是100
…z
是10000000000000000000000000
(25个0)。 -
|
是按位或:二进制编码中,每一位二者个中一个为1,则为1,不然,则为0,因而
val |=
就是对每一个字符兼并,比方ab
是00010|00001
=>00011
,f
是100000
,ffff
也是100000
,big
是101000010
,axdg
是100000000000000001001001
。 &
,按位与,二进制编码中,每一位二者都为1,则为1,不然,则为0,例1:
axdg
和oigd
要推断是不是有反复:axdg是:100000000000000001001001 oifd是: 100000100101000 & 后: 000000000000000000001000
由于第4位都为1,所以末了不为0,也可得知反复的就是字母表第4位:
d
。
例2:axdg
和lkmk
要推断是不是有反复:效果为0,申明无反复。
axdg是:100000000000000001001001 lkmk是: 1110000000000 & 后: 000000000000000000000000
总结:这类要领运用了二进制数字的位数作为保留字符的手腕,相比起数组,散列表等,速率更快,在保留量较小(<=32)上风异常显著。
代码:
/**
* @param {string[]} words
* @return {number}
*/
var maxProduct = function(words) {
let aCode='a'.charCodeAt(0)
function compute(word){
let val=0
for(let i=0;i<word.length;i++){
val |= (1 << (word.charCodeAt(i)-aCode))
}
return val
}
let pretreatment=[]
for(let i=0;i<words.length;i++){
pretreatment[i]=compute(words[i])
}
let maxSum=0
for(let i=0;i<words.length-1;i++){
for(let j=i+1;j<words.length;j++){
let len1=words[i].length,len2=words[j].length
if(len1*len2>maxSum && (pretreatment[i] & pretreatment[j])===0){
maxSum=len1*len2
}
}
}
return maxSum
};