【剑指Offer-字符串】面试题5:替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

思路1

如果不要求原地替换的话,设置一个新的字符串 ans,遍历输入的字符串 s,如果 s[i] 为空格,则 ans += "%20",否则的话 ans += s[i]。代码如下:

class Solution {
public:
    string replaceSpace(string s) {
        if(s.empty()) return s;

        string ans = "";
        for(int i=0; i<s.size(); i++){
            if(s[i]!=' ') ans += s[i];
            else ans += "%20";
        }
        return ans;
    }
};

思路2

一种比较容易想到的方法就是从左到右遍历该字符串,当碰到空格的时候就把空格替换为%20,因为从1个字符(' ')替换为了3个字符('%20'),所以要对空格后面的字符进行移动。假设字符串长度为n,对于每个空格字符,需要移动后面的O(n)的字符,因此对于含有O(n)个空格字符的字符串而言,总的时间复杂度为\(O(n^2)\)

思路1对应的代码如下

class Solution {
public:
    // str为输入的字符串,length为总的可用长度
	void replaceSpace(char *str,int length) {
        if(str==nullptr || length==0)
            return;
        
        int strLen = 0;    //字符串长度
        while(str[strLen]!='\0')
            strLen++;
        
        for(int i=0; i<strLen; i++){
            if(str[i]==' '){
                for(int j=strLen; j>i; j--)
                    str[j+2] = str[j];    //注意是加2
                str[i] = '%';
                str[i+1] = '2';
                str[i+2] = '0';
                strLen += 2;
            }
        }
          
	}
};

思路3

思路1的时间复杂度太高了,思路1是从左到右扫描字符串然后移动,这就导致了一些字符被移动多次。可以换一个思路,从右到左扫描字符串。首先分配好替换后字符串的空间,如果原来的字符串为n,共s个空格字符,那么替换后的字符串长度为n+2*s。我们设置两个指针,一个指向替换前的字符串末尾p1,另一个指向替换后的字符串末尾p2。两个指针都从右向左移动,首先对当前p1指向的字符进行判断:若p1指向的字符c1不是空格,那么将c1复制到p2指向的位置,p2向左移动一位;如果p1指向的字符c1是空格,那么将p2以及p2的左两位填充为%20,p2向左移动2位。最后,将p1向左移动一位,重复此过程,直至p1将替换前的字符串从右到左遍历一遍。假设字符串长度为n,因为所有的字符只移动1次,所以时间复杂度为O(n)。

思路2对应的代码如下

class Solution {
public:
	void replaceSpace(char *str,int length) {
        if(str==nullptr || length==0)
            return;
        
        int strLen = 0;
        int bsCnt = 0;    //空格个数
        for(int i=0; str[i]!='\0'; i++){
            strLen++;
            if(str[i]==' ')
                bsCnt++;
        }
        int newStrLen = strLen + 2*bsCnt;
        
        int cur1 = strLen;    //替换前的字符串长度
        int cur2 = newStrLen;    //替换后的字符串长度
        while(cur1>=0 && cur2>cur1){    //条件只写cur1>=0牛客网可以通过
            if(str[cur1]==' '){
                str[cur2--] = '0';
                str[cur2--] = '2';
                str[cur2--] = '%';
            }
            else str[cur2--] = str[cur1];
            cur1--;
        }
          
	}
};

如果输入是 string 的话,这样写

class Solution {
public:
    string replaceSpace(string s) {
        if(s.empty()) return s;

        int l1 = s.size();
        for(int i=0; i<l1; i++){
            if(s[i]==' ') s += "00"; // 注意是加两个零,不是3个
        }
        int l2 = s.size()-1;

        for(int i=l1-1; i>=0; i--){
            if(s[i]==' '){
                s[l2--] = '0';
                s[l2--] = '2';
                s[l2--] = '%';
            }else s[l2--] = s[i];
        }
        return s;
    }
};

总结

在合并两个字符串(或者数组时),如果从左到右复制每个字符(或数字),则需要重复移动字符(或数字)多次。这种情况下,可以考虑从右向左移动字符,这样能减少移动的次数,从而提高效率。

posted @ 2020-01-07 20:14  Flix  阅读(219)  评论(0编辑  收藏  举报