[LeetCode] Reverse Words in a String 翻轉字串中的單詞
Given an input string, reverse the string word by word.
For example,
Given s = "the sky is blue
",
return "blue is sky the
".
Update (2015-02-12):
For C programmers: Try to solve it in-place in O(1) space.
Clarification:
- What constitutes a word?
A sequence of non-space characters constitutes a word. - Could the input string contain leading or trailing spaces?
Yes. However, your reversed string should not contain leading or trailing spaces. - How about multiple spaces between two words?
Reduce them to a single space in the reversed string.
這道題讓我們翻轉字串中的單詞,題目中給了我們寫特別說明,如果單詞之間遇到多個空格,只能返回一個,而且首尾不能有單詞,並且對C語言程式設計師要求空間複雜度為O(1),所以我們只能對原字串s之間做修改,而不能宣告新的字串。那麼我們如何翻轉字串中的單詞呢,我們的做法是,先整個字串整體翻轉一次,然後再分別翻轉每一個單詞(或者先分別翻轉每一個單詞,然後再整個字串整體翻轉一次),此時就能得到我們需要的結果了。那麼這裡我們需要定義一些變數來輔助我們解題,storeIndex表示當前儲存到的位置,n為字串的長度。我們先給整個字串反轉一下,然後我們開始迴圈,遇到空格直接跳過,如果是非空格字元,我們此時看storeIndex是否為0,為0的話表示第一個單詞,不用增加空格;如果不為0,說明不是第一個單詞,需要在單詞中間加一個空格,然後我們要找到下一個單詞的結束位置我們用一個while迴圈來找下一個為空格的位置,在此過程中繼續覆蓋原字串,找到結束位置了,下面就來翻轉這個單詞,然後更新i為結尾位置,最後遍歷結束,我們剪裁原字串到storeIndex位置,就可以得到我們需要的結果,程式碼如下:
C++ 解法一:
class Solution { public: void reverseWords(string &s) { int storeIndex = 0, n = s.size(); reverse(s.begin(), s.end()); for (int i = 0; i < n; ++i) { if (s[i] != ' ') { if (storeIndex != 0) s[storeIndex++] = ' ';int j = i; while (j < n && s[j] != ' ') s[storeIndex++] = s[j++]; reverse(s.begin() + storeIndex - (j - i), s.begin() + storeIndex); i = j; } } s.resize(storeIndex); } };
Java解法一:
public class Solution { public String reverseWords(String s) { int storeIndex = 0, n = s.length(); StringBuilder sb = new StringBuilder(s).reverse(); for (int i = 0; i < n; ++i) { if (sb.charAt(i) != ' ') { if (storeIndex != 0) sb.setCharAt(storeIndex++, ' '); int j = i; while (j < n && sb.charAt(j) != ' ') sb.setCharAt(storeIndex++, sb.charAt(j++)); String t = new StringBuilder(sb.substring(storeIndex - (j - i), storeIndex)).reverse().toString(); sb.replace(storeIndex - (j - i), storeIndex, t); i = j; } } sb.setLength(storeIndex); return sb.toString(); } }
下面我們來看使用字串流類stringstream的解法,我們先把字串裝載入字串流中,然後定義一個臨時變數tmp,然後把第一個單詞賦給s,這裡需要注意的是,如果含有非空格字元,那麼每次>>操作就會提取連在一起的非空格字元,那麼我們每次將其加在s前面即可;如果原字串為空,那麼就不會進入while迴圈;如果原字串為許多空格字元連在一起,那麼第一個>>操作就會提取出這些空格字元放入s中,然後不進入while迴圈,這時候我們只要判斷一下s的首字元是否為空格字元,是的話就將s清空即可,參見程式碼如下:
C++ 解法二:
class Solution { public: void reverseWords(string &s) { istringstream is(s); string tmp; is >> s; while(is >> tmp) s = tmp + " " + s; if(!s.empty() && s[0] == ' ') s = ""; } };
下面這種方法也是使用stringstream來做,但是我們使用了getline來做,第三個引數是設定分隔字元,我們用空格字元來分隔,這個跟上面的>>操作是有不同的,每次只能過一個空格字元,如果有多個空格字元連在一起,那麼t會賦值為空字串,所以我們在處理t的時候首先要判斷其是否為空,是的話直接跳過,參見程式碼如下:
C++ 解法三:
class Solution { public: void reverseWords(string &s) { istringstream is(s); s = ""; string t = ""; while (getline(is, t, ' ')) { if (t.empty()) continue; s = (s.empty() ? t : (t + " " + s)); } } };
而如果我們使用Java的String的split函式來做的話就非常簡單了,沒有那麼多的么蛾子,簡單明瞭,我們首先將原字串呼叫trim()來去除冗餘空格,然後呼叫split()來分隔,分隔符設為"\\s+",這其實是一個正則表示式,\\s表示空格字元,+表示可以有一個或多個空格字元,那麼我們就可以把單詞分隔開裝入一個字串陣列中,然後我們從末尾開始,一個個把單詞取出來加入結果res中,並且單詞之間加上空格字元,注意我們把第一個單詞留著不取,然後返回的時候再加上即可,參見程式碼如下:
Java解法二:
public class Solution { public String reverseWords(String s) { String res = ""; String[] words = s.trim().split("\\s+"); for (int i = words.length - 1; i > 0; --i) { res += words[i] + " "; } return res + words[0]; } }
下面這種方法就更加的簡單了,瘋狂的利用到了Java的內建函式,這也是Java的強大之處,注意這裡的分隔符沒有用正則表示式,而是直接放了個空格符進去,後面還是有+號,跟上面的寫法得到的效果是一樣的,然後我們對字串陣列進行翻轉,然後呼叫join()函式來把字串陣列拼接成一個字串,中間夾上空格符即可,參見程式碼如下:
Java解法三:
public class Solution { public String reverseWords(String s) { String[] words = s.trim().split(" +"); Collections.reverse(Arrays.asList(words)); return String.join(" ", words); } }
類似題目:
參考資料: