Hi 你們好,我是張小豬。歡迎來到『寶寶也能看懂』系列之 leetcode 周賽題解。git
這裏是第 172 期的第 2 題,也是題目列表中的第 1324 題 -- 『豎直打印單詞』github
給你一個字符串 s
。請你按照單詞在 s
中的出現順序將它們所有豎直返回。
單詞應該以字符串列表的形式返回,必要時用空格補位,但輸出尾部的空格須要刪除(不容許尾隨空格)。
每一個單詞只能放在一列上,每一列中也只能有一個單詞。shell
示例 1:segmentfault
輸入:s = "HOW ARE YOU" 輸出:["HAY","ORO","WEU"] 解釋:每一個單詞都應該豎直打印。 "HAY" "ORO" "WEU"
示例 2:數組
輸入:s = "TO BE OR NOT TO BE" 輸出:["TBONTB","OEROOE"," T"] 解釋:題目容許使用空格補位,但不容許輸出末尾出現空格。 "TBONTB" "OEROOE" " T"
示例 3:優化
輸入:s = "CONTEST IS COMING" 輸出:["CIC","OSO","N M","T I","E N","S G","T"]
提示:spa
1 <= s.length <= 200
s
僅含大寫英文字母。MEDIUMcode
題目會給定一個包含多個單詞的字符串,其中每一個單詞都是由英文大寫字母組成,而且它們之間必定是由一個空格分隔開來。不過返回的結果直接看起來會怪怪的,由於是把每一個單詞從上到下縱向排列,而後各個單詞從左到右組合在一塊兒造成的一個字符串數組。也就是返回的數組中,第一個字符串是每一個單詞的第一個字母按照單詞從左到右出現的順序組成的,第二個字符串是每一個單詞的第二個字母,以此類推。blog
這裏須要特別注意的一點是,因爲每一個單詞的長度不同,因此返回的字符串可能會有補位空格,不過並不保留多餘的後綴空格。這一點應該就是這道題最容易弄錯的地方啦。關於空格的問題,能夠具體看看下面這個例子,應該就能明白:leetcode
那麼咱們接下來看看思路吧。首先從結果來看,從上面的例子咱們能夠看出,需求的這個字符串數組是能夠直接由每一個單詞的第一個字母、第二個字母...這樣子直接拼接起來的。而原始字符串中每一個單詞的分隔符固定是一個空格。因此很是容易能想到,咱們能夠先把原始字符串拆分紅單詞組,而後遍歷拼接便可。
思路大致成型以後,須要注意一下其中的細節,也就是特殊空格的處理。
因爲後面的單詞可能會比前面的長,因此對於前面的單詞,咱們是有可能須要去拼接補位空格的。例如上面圖中例子裏的那個 P R
,這裏就須要補充空格。
那是否意味着咱們能夠直接對於空缺的內容用空格來填充呢?固然並非的。由於當後面沒有較長的單詞的時候,咱們這時候若是再去拼接空格就會變成了空格後綴。而按照題目要求,這個類型的空格是須要去掉的。例如上面圖中例子裏的那個 Y
。
這個問題的處理相信小夥伴也很容易能想到,小豬這裏就不作過多分析啦。也就是咱們能夠把可能的空格先記錄下來,可是並不作拼接。在後續有須要的時候再做爲補位空格拼接進去便可。基於這個思路,具體流程以下:
根據最長的單詞長度,遍歷每一個單詞中對應的字符:
基於這個流程,咱們能夠實現相似這樣的代碼:
const printVertically = s => { const words = s.split(' '); let maxLen = 0; for (let i = 0; i < words.length; ++i) { if (words[i].length > maxLen) maxLen = words[i].length; } const ret = new Array(maxLen); for (let i = 0; i < maxLen; ++i) { ret[i] = ''; let prevSpace = ''; for (let j = 0; j < words.length; ++j) { words[j][i] === undefined ? (prevSpace += ' ') : ((ret[i] += prevSpace + words[j][i]), (prevSpace = '')); } } return ret; };
上面的流程裏,咱們會發現,其實存在着蠻屢次遍歷的,而且咱們還須要去拆分原始字符串爲單詞組。對於這一部分,咱們是否有辦法能夠優化呢?
回看剛纔的思路和代碼,咱們的作法是去取每一個單詞的第 n 個字符,而後直接組成結果裏的第 n 個字符串。若是把結果數組想象成是一個由字符組成的二維數組的話,這也就至關於咱們是一行一行的拼接結果的。因此這也就須要咱們先去拆出每一個單詞,並求得最長的單詞的長度。說到這裏,有的小夥伴可能會想到,除了一行一行的拼接以外,咱們是否能夠一列一列的拼接呢?
咱們來看看,若是是一列一列的拼接的話,那麼從第一列開始日後,其實不就是咱們的每一個單詞原本的順序麼,也就是咱們原始字符串原本的順序。那若是這樣拼接的話,就根本不須要去拆分單詞組,也不須要去根據各個單詞的字符來依次遍歷了。咱們直接從頭開始遍歷原始字符串不就行了麼。妙鴨。
不過這裏仍是須要注意特殊空格的問題。因爲咱們是直接遍歷原始字符串,因此在推動的過程當中,咱們並不知道是否應該去填補空格。例如對於前面圖中的那個例子,對於 "YEAR" 中的 "R",咱們會直接把它和 "P" 拼在一塊兒獲得 "PR",而不是 "P R"。那麼咱們如何解決這個問題呢?
仔細看上面 "R" 字母的例子,咱們爲何須要的是 "P R",是由於中間的那個單詞這裏是空缺的。換句話說就是,第一個單詞這裏有字母,第二個單詞這裏空缺,而第三個單詞這裏也有字母。再換句話說就是,對於第三個單詞這裏的字母,它指望的是前面已經有了兩個字母,也就是字符串的長度爲 2。因此當前面字符串的長度不足 2 的時候,咱們須要用空格去填充。說到這裏相信小夥伴們都明白了吧,咱們只須要根據當前字母所屬的是第幾個單詞,結合前面字符串的長度,便可完成對於補位空格的補充了。
基於這個思路,咱們須要加入一個變量,用於記錄當前遍歷到第幾個單詞。具體流程以下:
遍歷整個原始字符串:
基於這個流程,咱們能夠實現相似這樣的代碼:
const printVertically = s => { const ret = []; for (let i = 0, idx = 0, word = 0; i < s.length; ++i, ++idx) { const char = s[i]; if (char === ' ') { ++word; idx = -1; continue; } if (ret[idx] === undefined) { ret[idx] = ' '.repeat(word) + char; continue; } ret[idx].length !== word && (ret[idx] += ' '.repeat(word - ret[idx].length)); ret[idx] += char; } return ret; };
這段代碼以 36ms 暫時 beats 100%。
這是一道思路並不複雜的題,特別是其中直接方案的思路應該是能夠根據題目要求瓜熟蒂落獲得的。但是最後咱們卻發現,直接方案其實繞了一圈。而這些發現,正是來自於咱們對於一些例子和結果對應關係的觀察和思考。因此有時候真的不妨寫一些可能的結果出來康康,而後咱們就會發現其實它超勇的,由於答案就在裏面了。>.<
因此,小夥伴們要不要來給小豬康康鴨,小豬超勇的,哈哈哈哈嗝。