壹 ❀ 引
今天做的又是一道讓我沮喪的題,思路有,但是代碼邏輯最后還是沒能正確理出來,題名為最長公共前綴,題目如下:
編寫一個函數來查找字符串數組中的最長公共前綴。
如果不存在公共前綴,返回空字符串 ""。
示例 1:
輸入: ["flower","flow","flight"] 輸出: "fl"
示例 2:
輸入: ["dog","racecar","car"] 輸出: ""
解釋: 輸入不存在公共前綴。
說明:所有輸入只包含小寫字母 a-z 。
還是記錄下我的思路,雖然沒能成功做出來,但是思路很重要!
貳 ❀ 我的解題思路
題目示例其實說的很清楚了,給定一個包含多個字符串的數組,找出這些字符公共前綴,注意是前綴,也就是說從第一位字符開始就要相同才符合條件。
我的思路是這樣,首先將數組元素按字符長度由短到長排列(事實證明這步多此一舉)。
strs.sort((a,b)=>{return a.length-b.length});
經過排序,比如示例1就會變成["flow","flower","flight"]
,為什么說多此一舉,事實上完全會存在字符長度一樣的情況,比如flower
與flight
就一樣,排序的意義不大。
其實我是想讓最短的在前面,作為比較的參照元素,這樣for循環比較的時候 i 可以從1開始。
每次遍歷我都會讓strs[0]
作為正則匹配標准,如果有不滿足的,就讓strs[0]
的字符長度逐漸減少,一直比較完,通過這個來決定是返回 '' 或者是我們前面定義好的滿足條件的正則字符。
// 大概的意思
var len = strs[0].length;
for (var i = 1; i < strs.length; i++) {
var regexp = new Regexp(strs[0].substr(0,len));
if(strs[1].test(regexp)){
// 巴拉巴拉...
};
len -- ;
};
當然上述程序只是大概表示了我的意思,因為最后我在處理字符比較這塊,怎么都沒理出來,還是沒能成功做出來!!!蒼天啊!!!
所以我還是選擇看看答案,比較幸運的是,用戶大佬rhinoc給出的三種解決方案中,第三個實現與我思路類似,我先貼代碼:
/**
* @param {string[]} strs
* @return {string}
*/
var longestCommonPrefix = function (strs) {
// 判斷空數組情況,並直接將第一位元素作為參照物
var re = strs[0] ? strs[0] : '';
// 注意,這里遍歷是從1開始,因為第一位被我們拿來當參照物了
for (var i = 1; i < strs.length; i++) {
// 注意這里的正則加了^,表示從字符開始位置開始匹配
var regex = new RegExp('^' + re);
// 比較其它字符看是否符合,若不符合讓正則條件的字符遞減
while (!regex.test(strs[i]) && re.length) {
// 這里控制了字符遞減
re = re.slice(0, re.length - 1);
// 遞減后重新聲明正則
regex = new RegExp('^' + re);
};
};
return re;
};
看了大佬的實現,瞬間有種自己的美好夢想被別人代替實現了的錯覺....代碼上我簡單加了注釋,思路確實與我相同,但是我對於代碼的把控真的太差了。
首先,數組按字符長度排序沒必要,這點前面解釋了。其次,我本意讓第0位作為正則匹配條件,但是我的實現為new Regexp(strs[0].substr(0,len))
,並沒有加**脫字符**。表示一定要從字符起始位置開始匹配,不太明白這個符號的同學有興趣可以閱讀博主JS 正則表達式^$詳解這篇文章。
那么如果不加,就算我實現了,會掛在['ll', 'hello']
這樣的例子上,因為沒限制必須是起始位置,正則會認定hello
也包含了ll
。
我在實現上,沒把控好的點就是怎么讓正則test不符合條件時繼續比較,以及如何控制strs[0]字符長度的遞減(畢竟總不能一直遞減),想了半天,看了他人實現,一句!regex.test(strs[i]) && re.length
讓我瞬間清醒....
這里簡單復習下slice方法,slice(start,stop)中的start與stop均為索引,比如:
[1,2,3].slice(1);// [2,3]
[1,2,3].slice(0,1)// [1]
slice方法與上篇博客提到的substring方法有點像,都是含頭不含尾,即匹配結果的長度為stop-start,包含start但不包含stop。
OK,一些小細節解釋清楚了,我們來說說這段循環嵌套究竟做了什么。還是["flower","flow","flight"]
為例:
一開始,正則條件為flower,與flow比較,由於test為false,所以flower遞減,此時正則條件變為flowe。
flowe與flow再次比較,結果還是不通過,再次遞減正則條件變成flow。
終於flow與flow相同,由於不再滿足while循環條件,跳出循環,此時外層for循環的i自增。
於是正則條件flow接着與flight比較,由於不滿足,繼續遞減,變成flo,同理不滿足再遞減,最后變成了fl滿足了條件,while再次跳出循環,for也因為循環完畢,最后的re被遞減成了fl,被成功返回。
而再以例子["dog","racecar","car"]
來看,dog與racecar的比較過程中,由於始終不滿足條件,re字段最終會被slice成 '',所以這點確實很巧妙。
從一開始取出第1位字符作為參照條件,后面始終維護它,要么遞減過程中滿足了條件,要么遞減成 '' ,最后將它作為最終結果返回。這樣的思路確實比較清晰太多太多了!
那么關於本題就說到這里了。