《剑指Offer》面试题5-替换空格
题目:请实现一个函数,把字符串中的每个空格替换成"%20"。例如输入“We are happy.”,则输出“We%20are%20happy.”。
应用背景:在网络编程中,如果URL参数中含有特殊字符,如空格、'#'等,可能导致服务器端无法获得正确的参数值。我们需要将这些特殊符号转换成服务器可以识别的字符。转换的规则是在'%'后面跟上ASCII码的两位十六进制的表示。比如空格的ASCII码是32,即十六进制的0x20,因此空格被替换成"%20"。再比如'#'的ASCII码为35,即十六进制的0x23,它在URL中被替换为"%23"。
知识长进:
- 为了节省内存,C/C++把常量字符串放到单独的一个内存区域,当几个指针赋值给相同的常量字符串时,他们实际上会指向相同的内存。
- 在合并两个数组(包括字符串)时,如果从前往后复制每个数字则需要重复移动数字多次,那么可以逆向思维,考虑从后往前复制,这样子可以减少移动的次数,从而提高效率,但是要注意内存覆盖问题。
- 在清楚空格中,清除空格后所得到的字符串要比原先的字符串要短,我们从头到尾进行清楚空格的话就不会覆盖到空格后面的字符,所以我们可以从头开始清除,效率反而比较高。
相比源代码的改进点:因为C/C++中每个字符串都以字符‘\0’作为结尾,所以源代码中if(newLength > length)存在溢出风险,修正为if((newLength+1) > length)。
***********************************************************************************
算法实现:如果从前往后替换字符串,那么保存在空格后面的字符串肯定会被覆盖,那么我们就考虑从后往前进行替换。
- 首先遍历原字符串,找出字符串的长度以及其中的空格数量,
- 根据原字符串的长度和空格的数量我们可以求出最后新字符串的长度。
- 设置两个指针point1和point2分别指向原字符串和新字符串的末尾位置。
- 如果point1指向内容不为空格,那么将内容赋值给point2指向的位置,如果point1指向为空格,那么从point2开始赋值“%20”
- 直到point1==point2时表明字符串中的所有空格都已经替换完毕。
#include<cstdio> #include<cstring> /*length为字符数组string的总容量*/ void ReplaceBlank(char string[], int length) { //特殊输入,鲁棒性 if (string == nullptr || length <= 0) return; //计算空格的数量 int i = 0; int originalLength = 0; int numberOfBlank = 0; while (string[i]!='\0') //字符串结尾 { if (string[i] == ' ') numberOfBlank++; originalLength++; ++i; } /*newLength为把空格替换成'%20'之后的长度*/ int newLength = originalLength + 2 * numberOfBlank; //鲁棒性,自己未考虑到,考虑最后字符串的空格 if (newLength > (length-1)) return; int indexOfOriginal = originalLength; int indexOfNew = newLength; while (indexOfNew>indexOfOriginal && indexOfOriginal>=0) { if (string[indexOfOriginal] == ' ') { //空格,新字符串替换为‘%20’ string[indexOfNew--] = '0'; string[indexOfNew--] = '2'; string[indexOfNew--] = '%'; indexOfOriginal--; } else { string[indexOfNew--] = string[indexOfOriginal--]; } } } // ====================测试代码==================== void Test(char* testName, char str[], int length, char expected[]) { if (testName != nullptr) printf("%s begins: ", testName); ReplaceBlank(str, length); if (expected == nullptr && str == nullptr) printf("passed.\n"); else if (expected == nullptr && str != nullptr) printf("failed.\n"); else if (strcmp(str, expected) == 0) //字符串相等 printf("passed.\n"); else printf("failed.\n"); } // 空格在句子中间 void Test1() { const int length = 100; char str[length] = "hello world"; Test("Test1", str, length, "hello%20world"); } // 空格在句子开头 void Test2() { const int length = 100; char str[length] = " helloworld"; Test("Test2", str, length, "%20helloworld"); } // 空格在句子末尾 void Test3() { const int length = 100; char str[length] = "helloworld "; Test("Test3", str, length, "helloworld%20"); } // 连续有两个空格 void Test4() { const int length = 100; char str[length] = "hello world"; Test("Test4", str, length, "hello%20%20world"); } // 传入nullptr void Test5() { Test("Test5", nullptr, 0, nullptr); } // 传入内容为空的字符串 void Test6() { const int length = 100; char str[length] = ""; Test("Test6", str, length, ""); } //传入内容为一个空格的字符串 void Test7() { const int length = 100; char str[length] = " "; Test("Test7", str, length, "%20"); } // 传入的字符串没有空格 void Test8() { const int length = 100; char str[length] = "helloworld"; Test("Test8", str, length, "helloworld"); } // 传入的字符串全是空格 void Test9() { const int length = 100; char str[length] = " "; Test("Test9", str, length, "%20%20%20"); } int main(int argc, char* argv[]) { Test1(); Test2(); Test3(); Test4(); Test5(); Test6(); Test7(); Test8(); Test9(); getchar(); return 0; }