網頁

2018/12/21

LeetCode Reverse Words in a String III

本篇為LeetCode上演算法的簡單問題,Reverse Words in a String III,字串反轉III。

題目如下:

Given a string, you need to reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order.


Example 1:

Input: "Let's take LeetCode contest"
Output: "s'teL ekat edoCteeL tsetnoc"

Note:In the string, each word is separated by single space and there will not be any extra space in the string.


這題字串反轉是要將一段句子中的每個單字前後反轉就好,而不是將整段句子反轉。例如輸入"Let's take LeetCode contest",則輸出應為"s'teL ekat edoCteeL tsetnoc"。

此題一開始想到的解法就是用String.split()去分割句子中的每個單字為字串陣列,再將字串陣列中的每個單字反轉後重組。

public static String reverseWords(String s) {
    String[] ss = s.split("\\s");
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < ss.length; i++) {
        char[] cc =  ss[i].toCharArray();
        int lastIndex = cc.length - 1;
        for(int j = 0; j < cc.length/2; j++ ) {
            cc[j] ^= cc[lastIndex - j];
            cc[lastIndex - j] ^= cc[j];
            cc[j] ^= cc[lastIndex - j];
        }
        sb.append(" ").append(cc);
    }
    return sb.substring(1);
}

但這樣的解法效能不好,因為在String.split()中會跑迴圈,又加上外面用來返轉單字的迴圈,時間複雜度大於O(N)。


第二個解法是先用StringBuilder.reverse()先將整段句子反轉後再以String.split()進行分割並以對的順序重組。

public static String reverseWords(String s) {
    StringBuilder sb = new StringBuilder();
    String[] ss = sb.append(s).reverse().toString().split("\\s");
    int lastIndex = ss.length - 1;
    sb = new StringBuilder();
    for(int i = lastIndex; i >= 0; i--) {
        sb.append(" ").append(ss[i]);
    }
    return sb.substring(1);
}

雖然比第一次的解法快了一些,但仍只有36%的勝率。


第三個解法是參考多數人的寫法,從句子頭開始每個字元判斷,若該字元為空白,則將空白前的單字進行反轉,例用兩個變數來暫存每個單字的開頭和結束的索引位置,而句字的最後一個字要做額外的判斷。

public static String reverseWords(String s) {
        
    char[] cc = s.toCharArray();
    for(int end = 0, start = 0; end < cc.length; end++ ) {
        if(cc[end] == ' ') {
            reverseWord(start, end - 1, cc);
            start = end + 1;
        }
        if(end == cc.length - 1) {
           reverseWord(start, end, cc);
        }
    }
    return String.valueOf(cc);
}

public static void reverseWord(int start, int end, char[] cc) {
    while(start < end) {
        char temp = cc[start];
        cc[start] = cc[end];
        cc[end] = temp;
        start++;
        end--;
    }
}

此方法的beats率為65%。


下面的解法是例用StringBuilder.insert(int offset, char c)將每個字元插最前頭直到碰到空白,但效果也是不好。

public static String reverseWords(String s) {

    char[] cc = s.toCharArray();
    StringBuilder msb = new StringBuilder();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < cc.length; i++) {
        if (cc[i] == ' ') {
            msb.append(sb).append(" ");
            sb = new StringBuilder();
            i++;
        }
        sb.insert(0, cc[i]);
        if (i == cc.length - 1) {
            msb.append(sb.toString());
        }
    }
    return msb.toString();
}

參考:

沒有留言:

張貼留言