劍指Offer面試題:3.替換空格


一、題目:替換空格

題目:請實現一個函數,把字符串中的每個空格替換成"%20"。例如輸入“We are happy.”,則輸出“We%20are%20happy.”。 

  在網絡編程中,如果URL參數中含有特殊字符,如空格、'#'等,可能導致服務器端無法獲得正確的參數值。我們需要將這些特殊符號轉換成服務器可以識別的字符。轉換的規則是在'%'后面跟上ASCII碼的兩位十六進制的表示。比如空格的ASCII碼是32,即十六進制的0x20,因此空格被替換成"%20"。再比如'#'的ASCII碼為35,即十六進制的0x23,它在URL中被替換為"%23"。

二、解題思路

2.1 O(n2)的解法

  最直觀的做法是從頭到尾掃描字符串,每一次碰到空格字符的時候做替換。由於是把1個字符替換成3個字符,我們必須要把空格后面所有的字符都后移兩個字節,否則就有兩個字符被覆蓋了。下圖展示了從前往后把字符串中的空格替換成'%20'的過程:

  假設字符串的長度是n。對每個空格字符,需要移動后面O(n)個字符,因此對含有O(n)個空格字符的字符串而言總的時間效率是O(n2)。

2.2 O(n)的解法

  Step1.先遍歷一次字符串,這樣就能統計出字符串中空格的總數,並可以由此計算出替換之后的字符串的總長度。

  以前面的字符串"We arehappy."為例,"We are happy."這個字符串的長度是14(包括結尾符號'\0'),里面有兩個空格,因此替換之后字符串的長度是18。

  Step2.從字符串的后面開始復制和替換。

  准備兩個指針,P1和P2。P1指向原始字符串的末尾,而P2指向替換之后的字符串的末尾。接下來向前移動指針P1,逐個把它指向的字符復制到P2指向的位置,直到碰到第一個空格為止。接着向前復制,直到碰到第二、三或第n個空格。

  從上面的分析我們可以看出,所有的字符都只復制(移動)一次,因此這個算法的時間效率是O(n),比第一個思路要快。

三、解決問題

3.1 代碼實現

    public static void ReplaceBlank(char[] target, int maxLength)
    {
        if (target == null || maxLength <= 0)
        {
            return;
        }

        // originalLength 為字符串target的實際長度
        int originalLength = 0;
        int blankCount = 0;
        int i = 0;

        while (target[i] != '\0')
        {
            originalLength++;
            // 計算空格數量
            if (target[i] == ' ')
            {
                blankCount++;
            }
            i++;
        }

        // newLength 為把空格替換成'%20'之后的長度
        int newLength = originalLength + 2 * blankCount;
        if (newLength > maxLength)
        {
            return;
        }

        // 設置兩個指針,一個指向原始字符串的末尾,另一個指向替換之后的字符串的末尾
        int indexOfOriginal = originalLength;
        int indexOfNew = newLength;

        while (indexOfOriginal >= 0 && indexOfNew >= 0)
        {
            if (target[indexOfOriginal] == ' ')
            {
                target[indexOfNew--] = '0';
                target[indexOfNew--] = '2';
                target[indexOfNew--] = '%';
            }
            else
            {
                target[indexOfNew--] = target[indexOfOriginal];
            }

            indexOfOriginal--;
        }
    }

3.2 單元測試

  由於C#語言的特殊性,這里在測試初始化時做了一些特殊處理操作:

        const int maxLength = 100;
        char[] target = new char[maxLength];
        // Pre-Test
        [TestInitialize]
        public void ReplaceBlankInitialize()
        {
            for (int i = 0; i < maxLength; i++)
            {
                target[i] = '\0';
            }
        }

        public char[] GenerateNewTarget()
        {
            int length = 0;
            for (int i = 0; i < maxLength && target[i] != '\0'; i++)
            {
                length++;
            }

            char[] newTarget = new char[length];

            for (int i = 0; i < maxLength && target[i] != '\0'; i++)
            {
                newTarget[i] = target[i];
            }

            return newTarget;
        }
View Code

  (1)Test1:空格在句子中間

        // Test1:空格在句子中間
        [TestMethod]
        public void ReplaceBlankTest1()
        {
            // "hello world"
            target[0] = 'h';
            target[1] = 'e';
            target[2] = 'l';
            target[3] = 'l';
            target[4] = 'o';
            target[5] = ' ';
            target[6] = 'w';
            target[7] = 'o';
            target[8] = 'r';
            target[9] = 'l';
            target[10] = 'd';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "hello%20world";

            Assert.AreEqual(compared, expected);
        }
View Code

  (2)Test2:空格在句子開頭

        // Test2:空格在句子開頭
        [TestMethod]
        public void ReplaceBlankTest2()
        {
            // " helloworld"
            target[0] = ' ';
            target[1] = 'h';
            target[2] = 'e';
            target[3] = 'l';
            target[4] = 'l';
            target[5] = 'o';
            target[6] = 'w';
            target[7] = 'o';
            target[8] = 'r';
            target[9] = 'l';
            target[10] = 'd';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "%20helloworld";

            Assert.AreEqual(compared, expected);
        }
View Code

  (3)Test3:空格在句子末尾

        // Test3:空格在句子末尾
        [TestMethod]
        public void ReplaceBlankTest3()
        {
            // "helloworld "
            target[0] = 'h';
            target[1] = 'e';
            target[2] = 'l';
            target[3] = 'l';
            target[4] = 'o';
            target[5] = 'w';
            target[6] = 'o';
            target[7] = 'r';
            target[8] = 'l';
            target[9] = 'd';
            target[10] = ' ';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "helloworld%20";

            Assert.AreEqual(compared, expected);
        }
View Code

  (4)Test4:連續有兩個空格

        // Test4:連續有兩個空格
        [TestMethod]
        public void ReplaceBlankTest4()
        {
            // "helloworld "
            target[0] = 'h';
            target[1] = 'e';
            target[2] = 'l';
            target[3] = 'l';
            target[4] = 'o';
            target[5] = ' ';
            target[6] = ' ';
            target[7] = 'w';
            target[8] = 'o';
            target[9] = 'r';
            target[10] = 'l';
            target[11] = 'd';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "hello%20%20world";

            Assert.AreEqual(compared, expected);
        }
View Code

  (5)Test5:傳入NULL

        // Test5:傳入NULL
        [TestMethod]
        public void ReplaceBlankTest5()
        {
            target = null;
            Program.ReplaceBlank(target, 0);
            char[] expected = null;

            Assert.AreEqual(target, expected);
        }
View Code

  (6)Test6:傳入內容為空的字符串

        // Test6:傳入內容為空的字符串
        [TestMethod]
        public void ReplaceBlankTest6()
        {
            // ""
            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "";

            Assert.AreEqual(compared, expected);
        }
View Code

  (7)Test7:傳入內容為一個空格的字符串

        // Test7:傳入內容為一個空格的字符串
        [TestMethod]
        public void ReplaceBlankTest7()
        {
            // " "
            target[0] = ' ';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "%20";

            Assert.AreEqual(compared, expected);
        }
View Code

  (8)Test8:傳入的字符串沒有空格

        // Test8:傳入的字符串沒有空格
        [TestMethod]
        public void ReplaceBlankTest8()
        {
            // "helloworld "
            target[0] = 'h';
            target[1] = 'e';
            target[2] = 'l';
            target[3] = 'l';
            target[4] = 'o';
            target[5] = 'w';
            target[6] = 'o';
            target[7] = 'r';
            target[8] = 'l';
            target[9] = 'd';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "helloworld";

            Assert.AreEqual(compared, expected);
        }
View Code

  (9)Test9:傳入的字符串全是空格

        // Test9:傳入的字符串全是空格
        [TestMethod]
        public void ReplaceBlankTest9()
        {
            // "     "
            target[0] = ' ';
            target[1] = ' ';
            target[2] = ' ';
            target[3] = ' ';
            target[4] = ' ';

            Program.ReplaceBlank(target, maxLength);
            string compared = new string(this.GenerateNewTarget());
            string expected = "%20%20%20%20%20";

            Assert.AreEqual(compared, expected);
        }
View Code

  單元測試結果如下圖所示:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM