替換字符串中的空格

本文摘自《劍指offer》一書中的第4題:題目:請實現一個函數,把字符串中的每個空格替換成“%20”。例如輸入“We are happy”,則輸出“We%20are%20happy”。

看到這個題目,我們首先應該想到的是原來一個空格字符,替換之後變成'%,'2','0'這3個字符,因此字符串會變長。如果是是在原來的字符串上做替換,那麼就有可能覆蓋修改在該字符串後面的內存。如果是創建新的字符串並在新的字符串上座替換,那麼我們可以自己分配足夠多的內存(這種情況下更簡單,但一般面試不會是這種情況)。由於有兩種不同的解決方案,我們應該向面試官問清楚,讓他明確告訴我們他的需求。假設面試官讓我們在原來的字符串上座替換,並且保證輸入的字符串後面有足夠多的空餘內存。

解法一:直觀解法是從前往後掃面,沒遇到一個空格,就將空格後面的字符串後移,然後將空格替換爲%20,這樣做的時間複雜度太高爲O(n^2)。我們可以換一種思路,從尾部掃描字符串:

解法二:我們可以先遍歷一次字符串,這樣就能統計出字符串中空格的總數,並可以由此計算出替換之後的字符串的總長度。每替換一個空格,長度增加2,因此替換之後字符串的長度等於原來的長度加上2乘以空格數目。我們還是以前面的字符串“We are happy.”爲例,“We are happy”這個字符串的長度是14(包括結尾符號'\0'),裏面有兩個空格,因此替換之後字符串的長度是18。

我們從字符串的後面考試複製和替換。首先準備兩個指針,P1,P2。P1指向原始字符串的末尾,而P2指向替換之後的字符串的末尾。接下來我們想前移動指針P1,逐個把它指向的字符複製到P2指向的位置,知道碰到第一個空格位爲止。碰到第一個空格之後,把P1向前移動一格,在P2之前插入“%20”。由於“%20”的長度爲3,同時也要把P2向前移動3格。我們接着向前複製,知道碰到第二個空格。和上一次一樣,我們再把P1向前移動1格,並把P2向前移動3格插入“%20”。依次往復,直到P1指向字符串開頭的前一位置。

從上面分析我們可以看出,所有的字符都值複製(移動)一次,因此這個算法的時間效率是O(n),比直觀解法快。

在面試的過程中,我們可以動動筆畫畫圖來理清我們的思路,這樣不僅有助於我們編碼也有助於我們想面試官闡述我們的想法,代碼如下。

#include <iostream>
#include <string>
using namespace std;

#define MAXLEN 100 //存儲字符串的最大空間

//str爲要替換的字符串指針,strLen爲存儲字符串的空間
bool ReplaceBlank(char* str,int strLen)
{
	if(NULL == str || strLen <= 0)
		return false;
	int len = strlen(str);
	int blankCount = 0;
	int i = 0;
	for(i=0; i<len; i++)
	{
		if(str[i] == ' ')
			blankCount++;
	} 
	//替換後字符串的長度爲原長度+2倍的空格數
	int newLen = len + 2*blankCount;
	//如果替換後字符串需要的空間超出可以容納字符串的最大空間,則直接退出。
	if(newLen >= strLen)
		return false;

	//置ahead和behind爲len和newLen是因爲還要複製字符傳的結束符'\0'
	int ahead = len;
	int behind = newLen;
	//將字符串從後往前掃描並複製
	while(ahead >= 0)
	{
		//ahead位置不是空格,就直接複製到後面behind處
		if(str[ahead] != ' ')
		{
			str[behind--] = str[ahead--];
		}//否則behind處進行替換操作,並修改behind和ahead的位置。
		else
		{
			str[behind--] = '0';
			str[behind--] = '2';
			str[behind--] = '%';
			ahead--;
		}
	}
	
	return true;
}


int main()
{
	//char arr[MAXLEN] = "adf  adfj  adf ";
	//char arr[MAXLEN] = "";
	//char arr[MAXLEN] = "    ";
	char arr[MAXLEN] = "abcdeftg";
	cout<<arr<<endl;
	if(ReplaceBlank(arr,100))
		cout<<arr<<endl;
	return 0;
}

與此題相關的題目:有兩個排序的數組A1和A2,內存在A1的末尾有足夠多的空餘空間容納A2。請實現一個函數,把A2中的所有數字插入到A1中並且所有的數字是排序的(先計算出排序後總的空間大小:(A1+A2)空間的總和,然後從A1和A2的末尾開始比較並插入A1的末尾)。

舉一反三:合併兩個數組(包括字符串)時,如果從前往後複製每個數字(或字符)需要重複移動輸死(或字符)多次,那麼我們可以考慮從後往前複製,這樣就能減少移動的次數,從而提高效率。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章