JS leetcode 最長公共前綴 題解分析


壹 ❀ 引

今天做的又是一道讓我沮喪的題,思路有,但是代碼邏輯最后還是沒能正確理出來,題名為最長公共前綴,題目如下:

編寫一個函數來查找字符串數組中的最長公共前綴。

如果不存在公共前綴,返回空字符串 ""。

示例 1:

輸入: ["flower","flow","flight"]
輸出: "fl"

示例 2:

輸入: ["dog","racecar","car"]
輸出: ""

解釋: 輸入不存在公共前綴。
說明:

所有輸入只包含小寫字母 a-z 。

還是記錄下我的思路,雖然沒能成功做出來,但是思路很重要!

貳 ❀ 我的解題思路

題目示例其實說的很清楚了,給定一個包含多個字符串的數組,找出這些字符公共前綴,注意是前綴,也就是說從第一位字符開始就要相同才符合條件。

我的思路是這樣,首先將數組元素按字符長度由短到長排列(事實證明這步多此一舉)。

strs.sort((a,b)=>{return a.length-b.length});

經過排序,比如示例1就會變成["flow","flower","flight"],為什么說多此一舉,事實上完全會存在字符長度一樣的情況,比如flowerflight就一樣,排序的意義不大。

其實我是想讓最短的在前面,作為比較的參照元素,這樣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位字符作為參照條件,后面始終維護它,要么遞減過程中滿足了條件,要么遞減成 '' ,最后將它作為最終結果返回。這樣的思路確實比較清晰太多太多了!

那么關於本題就說到這里了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM