题目 leetcode151
题目链接 https://leetcode.cn/problems/reverse-words-in-a-string/
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s 中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
1 2 3 4 5 6 7 8 9 10 11 12 13
| 示例 1: 输入:s = "the sky is blue" 输出:"blue is sky the"
示例 2: 输入:s = " hello world " 输出:"world hello" 解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3: 输入:s = "a good example" 输出:"example good a" 解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
|
方案1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Solution { public: int find_space(string&s,int begin=0) { int i=0; for(i=begin+1;i<s.size();i++) { if(s[i]==' '){ return i; } } return s.npos; }
string reverseWords(string s) { int i=0; vector<string> v; for(i=0;i<s.size();i++) { while(s[i]==' '){ i++; } if(i>=s.size()){ break; } int index = find_space(s,i); int sz = index-i; string temp(s,i,sz); v.push_back(temp); i = index-1; } auto it = v.rbegin(); string ret = ""; while(it!=v.rend()) { ret+=*it; if(++it!=v.rend()){ ret+=" "; } } cout << ret << endl; return ret; } };
|
上面的办法中,新增了一个vector来拼接字符串,空间复杂度O(N)
测试 4MS 7.2MB
方案2
进阶要求:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1)
额外空间复杂度的 原地 解法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| class Solution { public: #define SEP '|' int find_space(string&s,int begin=0) { int i=0; for(i=begin+1;i<s.size();i++) { if(s[i]==' '){ break; } else if(s[i]==SEP) { break; } } int k = i; if(s[k]!=SEP) { while(s[k]==' ') { k++; if(s[k]==SEP){ return s.npos; } } return i; }
return s.npos; }
string reverseWords(string s) { int i=0,end_word=0,index=0; int begin_sz = s.size(); s+=SEP; int sep_index = s.size()-1; for(i=0;i<begin_sz;i++) { while(s[i]==' '){ i++; } if(i>=begin_sz){ break; } index = find_space(s,i); if(index == s.npos){ end_word=i; break; } int sz = index-i; string temp(s,i,sz); temp+=" "; s.insert(sep_index+1,temp);
i = index-1; } cout <<"[end]"<< s << endl; index = s.find(SEP); int k = s.find(" ",end_word); if(k!=s.npos&&k<index){ s.replace(k,1,""); index--; while(s[k]==' '){ s.replace(k,1,""); index--; } } s.replace(index,1," "); s.erase(0,end_word); s.erase(s.size()-1,1); return s; } };
|
结果 4MS 7MB
方案3
上面的办法看起来很对,实际上还是不是原地算法!
因为我每次都是在后面尾随字符串,而没有把原字符串删掉。如果源字符串很长,我这么做就相当于把源字符串的长度变为原有俩倍
要想做到真原地,就需要在插入到指定位置的同时,将其原本的字符串给删除了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| class Solution { public: #define SEP '|' int find_space(string&s,int begin=0) { int i=0; for(i=begin+1;i<s.size();i++) { if(s[i]==' '){ break; } else if(s[i]==SEP) { break; } } int k = i; if(s[k]!=SEP) { while(s[k]==' ') { k++; if(s[k]==SEP){ return s.npos; } } return i; }
return s.npos; }
string reverseWords(string s) { int i=0,end_word=0,index=0; int begin_sz = s.size(); s+=SEP; int sep_index = s.size()-1; for(i=0;i<begin_sz;i++) { while(s[i]==' '){ i++; } if(i>=begin_sz){ break; } index = find_space(s,i); if(index == s.npos){ end_word=i; break; } int sz = index-i; string temp(s,i,sz); temp+=" "; s.insert(sep_index+1,temp);
s.erase(i,sz); sep_index-=sz;
}
index = s.find(SEP); int k = s.find(" ",0); while(k!=s.npos&&k<index){ s.replace(k,1,""); index--; while(s[k]==' '){ s.replace(k,1,""); index--; } k = s.find(" ",0); } s[index]=' '; s.erase(s.size()-1,1); return s; } };
|
方案4,来自代码随想录P92
删除空格
这里对删除多余空格的代码做说明
1
| _ _ a b c _ _ h e l l o _
|
对上述字符串而言,fast首先会走到第一个非空格的字符a,然后开始复写。
对于两个单词中的多余空格,fast会复制第一个给slow,并跳过剩余的空格。所以这里判断的是fast-1
和fast
位置相等且都是空格的时候才会跳过。
1 2 3 4 5
| if(fast > 1 && s[fast-1] == s[fast] && s[fast]==' '){ continue; }
|
对于末尾的空格而言,最后遍历的时候fast的位置如下时,它会将这个空格复制给slow的位置(因为判断多余空格是判断fast和fast-1元素相等的时候才认为是多余空格的)
1 2 3 4 5 6
| 0 1 2 3 4 5 6 7 8 9 0 1 2 _ _ a b c _ _ h e l l o _ f a b c _ h e l l o _ l o _ s f
|
复制完毕后,slow和fast都会加一,位置变化如下,fast指针越界退出循环,slow位置是在空格的下一位。
1 2 3
| 0 1 2 3 4 5 6 7 8 9 0 1 2 a b c _ h e l l o _ l o _ s f
|
所以此时只需要判断slow-1的位置是不是空格,如果是空格,则新的字符串的大小是size-1
,如果不是空格,那么slow就是新字符串的大小。
1 2 3 4 5 6 7 8
| if(slow>1 && s[slow-1]==' ') { s.resize(slow-1); } else { s.resize(slow); }
|
完整代码
其他部分的代码说明详见注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| class Solution { public: void removeSpace(string& s) { int slow=0,fast=0; while(s.size()>0 && fast< s.size()&&s[fast]==' '){ fast++; } for(;fast<s.size();fast++) { if(fast > 1 && s[fast-1] == s[fast] && s[fast]==' ') continue; else { s[slow]=s[fast]; slow++; } } if(slow>1 && s[slow-1]==' ') { s.resize(slow-1); } else { s.resize(slow); } } void revserseString(string&s,int start,int end) { assert(end<s.size()&&start<=end); int i=start,j=end; for(;i<j;i++,j--) { swap(s[i],s[j]); } }
string reverseWords(string s) { removeSpace(s); revserseString(s,0,s.size()-1); int begin=0,cur=1; while(cur<s.size() && begin<s.size()) { while(cur!=begin&&s[cur]!=' '&&cur<s.size()) { cur++; } revserseString(s,begin,cur-1); begin= cur+1; cur+=2; } return s; } };
|
测试 4MS 6.8MB
一定要记得删除printf
以后写oj的时候,一定要记得删除用来debug的printf,否则用时特别特别长!
如下相同代码,删除pirntf之后快了十倍