替換空格
題目:請實現一個函數,將一個字符串中的空格替換成“%20”。例如,當字符串為We Are Happy.則經過替換之後的字符串為We%20Are%20Happy。
解析:如果從前往後替換字符串中的空格,則每次替換,都要把空格後的字符向後挪動相應的位置。這樣,時間復雜度會很高。在此,我們可以換個思路,先計算出替換後的字符串長度,然後從字符串後面向前遍歷,依次替換,這樣就不需要重復挪動字符了。
原因:在網絡編程中,如果URL參數中含有特殊字符,如:空格、“#”等,可能導致服務器端無法獲得正確的參數值。我們需要將這些特殊符號轉換成服務器識別的字符。轉換規則是在“%”後面跟上ASCII碼的兩位十六進制的表。比如:空格的ASCII瑪是32,即十六進制的0x20,因此空格被替換成“%20”。
時間復雜度為O(n2)不足以拿到Offer
現在我們考慮怎麽做替換操作。最直觀的做法是從頭到尾掃描字符串,每一次碰到空格字符的時候做替換。由於是把1個字符替換稱3個字符,我們必須把空格後面的所有的字符都後移兩個字節,否則就有兩個字符被覆蓋了。
舉個例子,我們從頭到尾把“We are happy"中的每一個空格替換成”%20“。為了形象期間,我們可以用一個表格來表示字符串,表格中的每個格子表示一個字符。如下圖所示:
我們替換了第一個空格,這個字符串編程圖b中的內容,表格中灰色背景表示需要移動的字符。接著我們替換第二個空格,替換之後的內容如圖c所示。同時我們註意到用深灰色的北京標註”happy“部分被移動了兩次。
假設字符的長度是n。對每個空格字符,需要移動後面O(n)個字符,因此對含有O(n)個空格字符串而言總的時間效率是O(n2).
當我們把這種思路闡述給面試官後,他不會就此滿意,他將讓我們尋找更快的方法。在前面的分析中,我們發現數組中的很多字符都移動了很多次,能不能減少移動的次數呢?我們換一種思路,把從前向後替換成從後向前替換。
考慮時間復雜度為O(n)的解法,搞定Offer就靠它了
我們先遍歷一次字符串,這樣就能夠統計出字符串中空格的綜述,並可以計算出替換之後字符串的總的長度。每替換一個空格,長度增加2,因此替換以後的字符串的長度等於原來的長度加上2乘以空格的數目,我們還是以前面的字符串”We are happy"為例,“We are happy"這個字符串的長度是14,裏面有兩個空格,因此替換之後的字符串的長度為18
我們從字符串的後面開始復制和替換。首先準備兩個指針,P1和P2. P1指向原始字符串的末尾,而P2指向替換之後的字符串的末尾。接下來我們向前移動指針P1,逐個把它指向的字符復制到P2指向的位置,直到碰到第一個空格為止。此時字符串包含如下圖b所示,灰色陰影的區域是做了字符拷貝的區域。碰到第一個空格之後,把P1向前移動一格,在P2之前插入字符串”%20“,由於”%20“的長度為3,同時也要把P2向前移動3格如圖所示。
我們接著向前復制,直到碰到第二個空格(d)所示。和上一次一樣,我們再把P1向前移動1格,並把P2向前移動3格插入”%20“(如圖e),此時P1,P2指向同一個位置,表明所有的空格都已經替換完畢。
從上面的分析我們可以看出,所有的字符都只復制一次,因此這個算法的時間效率是O(n),比第一個思路要快。
public class ReplaceSpaces { //計算字符串中包含的空格個數 public static int getBlankNum(String testString){ int count = 0 ; for(int i = 0;i<testString.length();i++){ String tempString = String.valueOf(testString.charAt(i)); if(tempString.equals(" ")){ count++; } } return count; } //打印char[] 數組 public static void printArray(char[] testArray){ for(char i :testArray){ System.out.print(i); } System.out.println(); } //將字符串空格轉化為20% public static void replaceAllBlank(String testString){ if(testString == null || testString.length() <= 0){ return; } //字符數組初始長度 int length = testString.length(); //字符數組增加長度後 int newLength = testString.length()+ getBlankNum(testString)*2; char[] tempArray = new char[newLength]; System.arraycopy(testString.toCharArray(), 0, tempArray, 0, testString.toCharArray().length); int indexofOriginal = length - 1; int indexofNew = newLength -1; System.out.println("未替換空格時的字符串:"); printArray(testString.toCharArray()); while(indexofOriginal >=0 && indexofOriginal != indexofNew){ if(tempArray[indexofOriginal]==‘ ‘){ tempArray[indexofNew--]=‘0‘; tempArray[indexofNew--]=‘2‘; tempArray[indexofNew--]=‘%‘; }else{ tempArray[indexofNew--]= tempArray[indexofOriginal]; } indexofOriginal--; } System.out.println("替換空格後的字符串:"); printArray(tempArray); } public static void main(String[] args){ String str = "We are happy"; replaceAllBlank(str); } }
替換空格