训练题:正则表达式匹配

题目:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/

方法一:递归寻路

正则表达式有点像寻路,当前位置有多种匹配可能,相当于有多条路,如果一条路行不通还得回来走另一条路。
这里的多条路的情况就是.或者a的匹配情况,其他情况都是只有一条路也就是要么能匹配,要么就是不能匹配。这里我们就详细讨论有*号的情况。

  1. // -: 用来表示任意字符或者为空
  2. // 源字符 正则字符
  3. // --a-- --a*--
  4. // --a-- --.*--
  5. // 这两种情况本质就是一种情况,可以统一看待。
  6. // 有两条路:先走一条,走不通,返回来走另一条。
  7. // 第一条: a*选择匹配a,则继续遍历源字符串下一个字符,正则串则不移动a*继续匹配下一个字符。
  8. // 第二条: a*选择不匹配a,则源字符串不移动,正则串移动到下下个字符(跳过*号)
  1. bool isMatch(string s, string p) {
  2. return match(s, p, s.size(), p.size(), 0, 0);
  3. }
  4. bool match(const string&s, const string&p, const int& size_s, const int& size_p, int idx_s, int idx_p){
  5. // 完整匹配的情况就是双双都遍历到结尾。
  6. if(idx_s == size_s && idx_p >= size_p) return true; // 源字符串和正则串都到结尾,完全匹配。
  7. // 正则串当前遍历位置的下一位是不是*号。
  8. bool nextIsStar = idx_p+1 < size_p && p[idx_p+1] == '*';
  9. // 源字符串与正则串当前位置是否匹配。
  10. bool matched = idx_s < size_s && idx_p < size_p && (s[idx_s] == p[idx_p] || p[idx_p] == '.');
  11. // 正则串的下一位是*
  12. if(nextIsStar) {
  13. //
  14. return
  15. // 选择第一条路
  16. (matched && match(s, p, size_s, size_p, idx_s+1,idx_p)) ||
  17. // 选择第二条路
  18. match(s, p, size_s, size_p, idx_s,idx_p+2);
  19. }
  20. // 就是肯定不是*的情况,也就是必须精确匹配。
  21. return matched && match(s, p, size_s, size_p, ++idx_s, ++idx_p);
  22. }

优化

  1. bool isMatch(string s, string p) {
  2. return match(s, p, s.size(), p.size(), 0, 0);
  3. }
  4. bool match(const string&s, const string&p, const int& size_s, const int& size_p, int idx_s, int idx_p){
  5. if(idx_s == size_s && idx_p >= size_p) return true; // 源字符串和正则串都到结尾,完全匹配。
  6. // 正则串当前遍历位置的下一位是不是*号。
  7. bool nextIsStar = idx_p+1 < size_p && p[idx_p+1] == '*';
  8. // 源字符串与正则串当前位置是否匹配。
  9. bool matched = idx_s < size_s && idx_p < size_p && (s[idx_s] == p[idx_p] || p[idx_p] == '.');
  10. // 正则串的下一位是*
  11. if(nextIsStar) {
  12. return (matched && match(s, p, size_s, size_p, idx_s+1,idx_p)) || match(s, p, size_s, size_p, idx_s,idx_p+2);
  13. }
  14. return matched && match(s, p, size_s, size_p, ++idx_s, ++idx_p);
  15. }

训练题:字符串排列

题目:http://t.cn/A6qccxH9

排列组合,首先想到回溯算法。

  1. // 回溯算法结构
  2. void backtrack( ... )
  3. {
  4. if("终止条件") {
  5. ...; // 可选逻辑
  6. return;
  7. }
  8. for(i = start; i != end; ++i){
  9. ......; // 可选逻辑
  10. ......; // 做出选择,设置新参数
  11. backtrack( "新参数" );
  12. ......; // 可选逻辑
  13. ......; // 撤销选择,参数恢复。
  14. }
  15. }
  1. vector<string> permutation(string s) {
  2. if(s.size() > 8 || s.empty()) return {};
  3. vector<string> ret;
  4. const char size = static_cast<char>(s.size());
  5. backtrack(s, ret, size, 0);
  6. return ret;
  7. }
  8. //
  9. // ["abc"]的全排列 = a为首的全排列 + b为首的全排列 + c为首的全排列
  10. //
  11. // 递归子过程:a为首的全排列 = a为首 + [b、c为次首]的全排列
  12. //
  13. // ["abb"]的全排列 = a为首的全排列 + b为首的全排列 + b为首的全排列(与前一个重复,去除)
  14. //
  15. // backtrack为求[start, ..., end]的全排列
  16. // [start, ..., end]的全排列 = [start, ..., end]中每个字符为首的全排列之和。
  17. // 注意[start, ..., end]中字符可能重复,重复的只考虑其中一个。
  18. void backtrack(string& s, vector<string>& container, const char& size, char start)
  19. {
  20. // 最后一位,只有一个排列,可确定。
  21. if(start == size - 1) {
  22. ret.push_back(s);
  23. return;
  24. };
  25. // 记录首字符的情况,避免重复首字符。这里的首位就是start位置
  26. set<int> fuck;
  27. for(char i = start; i < size; ++i) {
  28. if(fuck.count(s[i])) continue; // 有重复首字符,直接跳过
  29. // 求以s[i]为首字符的全排列
  30. fuck.insert(s[i]);
  31. std::swap(s[start], s[i]); // 将s[i]换到首位
  32. backtrack(s, container, size, start + 1); // 再递归次首,即start+1
  33. std::swap(s[start], s[i]); // 统计完之后记得恢复位置
  34. }
  35. }

训练题:字符串转数字

题目:http://t.cn/A6597Vhr

  1. int strToInt(string str) {
  2. int ret = 0;
  3. do
  4. {
  5. const auto size = str.size();
  6. int start = 0; // 遍历索引
  7. char positive = 1; // 正负
  8. char tmpNum = 0;
  9. // 过滤头部空字符
  10. for(; start < size && str[start] == ' '; ++start);
  11. if(start == size) break;
  12. // 过滤头部的正负号
  13. if(str[start] == '-'){
  14. positive = -1;
  15. ++start;
  16. }
  17. else if(str[start] == '+') ++start;
  18. // 转换有效部分
  19. for(; start < size && str[start] <= '9' && str[start] >= '0'; ++start) {
  20. // 转化成数字
  21. tmpNum = str[start] - '0';
  22. if(ret > 214748364) return positive > 0 ? 2147483647 : -2147483648;
  23. // 溢出判断
  24. else if (ret == 214748364){
  25. if(tmpNum >= 7 && positive > 0) return 2147483647;
  26. else if(tmpNum >= 8 && positive < 0) return -2147483648;
  27. }
  28. ret = ret * 10 + tmpNum;
  29. }
  30. ret *= positive;
  31. } while(false);
  32. return ret;
  33. }

训练题:左旋转字符串

题目:http://t.cn/A6qdlFB8

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  1. string reverseLeftWords(string s, int k) {
  2. if(s.size() <= k || s.empty() || k == 0) return s;
  3. reverse(s.begin(), s.begin() + k);
  4. reverse(s.begin() + k, s.end());
  5. reverse(s.begin(), s.end());
  6. return s;
  7. }

训练题:第一个只出现一次字符

题目:http://t.cn/A6qNxrTf

方法一:哈希表

  1. char firstUniqChar(string s) {
  2. if(s.empty()) return ' ';
  3. unordered_map<char, bool> dic;
  4. for(char c : s) dic[c] = dic.find(c) == dic.end();
  5. for(char c : s) if(dic[c]) return c;
  6. return ' ';
  7. }

方法二:哈希表+队列

  1. char firstUniqChar(string s) {
  2. if(s.empty()) return ' ';
  3. unordered_map<char, bool> dic; // char:字符,bool:字符是否重复
  4. queue<char> q; // 队列表头一定是不重复的字符。
  5. for(auto &c : s){
  6. if(!dic.count(c)){
  7. dic[c] = false;
  8. q.push(c);
  9. }
  10. else{
  11. dic[c] = true;
  12. // 每当遍历到重复字符,就刷新一次队列,确保队列头一直是不重复的字符。
  13. for(; !q.empty() && dic[q.front()]; q.pop());
  14. }
  15. }
  16. return q.empty() ? ' ' : q.front();
  17. }

训练题:判断数值

题目:http://t.cn/A6VqRjxk

方法一

  • 1、先按e/E分解字符串
  • 2、对e的前面按.号分解
    • 1、对.号的前面判断是否是整数
    • 2、对.号的后面判断是否是整数(不能有+/-号的)
  • 3、对e的后面判断是否是整数 ```cpp

bool isNumber( string s ) { if( s.empty() ) { return false; }

  1. const auto size = s.size();
  2. char startIdx = 0; // 字符串的有效区域起始和结尾。
  3. char endIdx = s.size() - 1; // 有效区域即指:头部去除空格和第一个+/-号,以及尾部去除空格。
  4. // 缩减掉两头的空格和开头的第一个+/-号(这么做是考虑+.8的奇葩情况)
  5. while( startIdx < size && s[startIdx] == ' ' ) { ++startIdx; }
  6. if( s[startIdx] == '+' || s[startIdx] == '-' ) { ++startIdx; }
  7. if( startIdx == size ) { return false; } // 没有有效区域
  8. while( endIdx >= 0 && s[endIdx] == ' ' ) { --endIdx; }
  9. // 找出第一个e的位置。
  10. char idx_e = startIdx;
  11. for( ; idx_e <= endIdx && s[idx_e] != 'e' && s[idx_e] != 'E'; ++idx_e );
  12. // 没有找到e,就判断有效区域是不是一个整数/小数。
  13. if( idx_e > endIdx ) { return _isNumber( s, startIdx, endIdx ); }
  14. // 如果找到e,就分别判断e的前后:
  15. // e的前面:是不是一个整数/小数
  16. // e的后面:是不是一个整数
  17. return _isNumber( s, startIdx, idx_e - 1 ) && _isInteger( s, true, idx_e + 1, endIdx );

}

// 判断是不是一个整数/小数(不带e) bool _isNumber( const string &s, char start, char end ) { if( start > end ) { return false; }

  1. // 找到第一个'.'号
  2. char idx_dot = start;
  3. for( ; idx_dot <= end && s[idx_dot] != '.'; ++idx_dot );
  4. // 找到了第一个'.'号,
  5. if( idx_dot <= end )
  6. {
  7. // 特殊情况'.'号在第一位,只需判断后面是不是整数(不带e,不带+/-号的)
  8. if( idx_dot == start ) { return _isInteger( s, false, idx_dot + 1, end ); }
  9. // 特殊情况'.'在最后一位,只需判断前面是不是整数(不带e,不带+/-号的)
  10. else if( idx_dot == end ) { return _isInteger( s, false, start, idx_dot - 1 ); }
  11. // 一般情况,就判断前后两半部分是不是整数(不带e,不带+/-号的)
  12. else { return _isInteger( s, false, start, idx_dot - 1 ) && _isInteger( s, false, idx_dot + 1, end ); }
  13. }
  14. // 没找到.号,就判断是不是整数
  15. return _isInteger( s, false, start, end );

}

// 判断是不是一个整数(不带e的,可以是带+/-号的) // sign:true表示开头可以有+/-号 bool _isInteger( const string &s, const bool &sign, char start, char end ) { if( sign && start <= end && ( s[start] == ‘+’ || s[start] == ‘-‘ ) ) { ++start; } if( start > end ) { return false; }

  1. // 找到第一个非数字的字符
  2. for( ; start <= end & s[start] >= '0' && s[start] <= '9'; ++start );
  3. return start > end;

}

  1. <a name="x8IIv"></a>
  2. ## 方法二:有限状态机
  3. <a name="CH7Lf"></a>
  4. # 训练题:字符串替换
  5. 题目:[http://t.cn/A62bGwLd](http://t.cn/A62bGwLd)
  6. ```cpp
  7. string replaceSpace(string s) {
  8. if( s.empty() ) { return ""; }
  9. const auto oldSize = s.size(); // 原始字符串长度
  10. int count = 0; // 空格数量
  11. for( auto &shit : s ) if( shit == ' ' ) { ++count; }
  12. if( count )
  13. {
  14. // 将string resize成newSize,刚好装下替换后的字符串。
  15. int newSize = oldSize + ( count << 1 );
  16. s.resize( newSize );
  17. // 倒序修改原字符串
  18. for( int oldIdx = oldSize - 1, newIdx = newSize - 1; oldIdx >= 0 && count; --oldIdx )
  19. {
  20. if( s[oldIdx] == ' ' )
  21. {
  22. s[newIdx--] = '0';
  23. s[newIdx--] = '2';
  24. s[newIdx--] = '%';
  25. --count;
  26. }
  27. else { s[newIdx--] = s[oldIdx]; }
  28. }
  29. }
  30. return s;
  31. }

训练题:括号序列

题目:http://t.cn/A6VZjsC7

方法一:栈

  • 时间复杂度:O(n)
  • 空间复杂度:O(n) ```cpp

bool isValid(string s) { stack stk;

  1. for( auto &c : s )
  2. {
  3. switch( c )
  4. {
  5. case '(':
  6. stk.push( ')' );
  7. break;
  8. case '[':
  9. case '{':
  10. stk.push( c + 2 );
  11. break;
  12. case ']':
  13. case ')':
  14. case '}':
  15. {
  16. if( !stk.empty() && stk.top() == c ) { stk.pop(); }
  17. else { return false; }
  18. }
  19. break;
  20. }
  21. }
  22. return stk.empty();

}

  1. <a name="Hrz6t"></a>
  2. ## 方法二:数组
  3. - 时间复杂度:O(n)
  4. - 空间复杂度:O(n)
  5. 其实就是用数组代替栈。
  6. ```cpp
  7. bool isValid(string s) {
  8. char stk[100000] = {0};
  9. const auto size = s.length();
  10. // top表示栈顶索引。
  11. for( int i = 0, top = -1; i < size; ++i )
  12. {
  13. switch( s[i] )
  14. {
  15. case '(':
  16. stk[++top] = ')';
  17. break;
  18. case '[':
  19. case '{':
  20. stk[++top] = s[i] + 2;
  21. break;
  22. case ']':
  23. case ')':
  24. case '}':
  25. {
  26. if( top > -1 && stk[top] == s[i] ) { stk[top--] = 0; }
  27. else { return false; }
  28. }
  29. break;
  30. }
  31. }
  32. return !stk[0]; // 栈是否为空。
  33. }

训练题:最长无重复串

情形一:int类型元素

题目:http://t.cn/A6VzlZjp

方法一:set保存不重复串

  1. int maxLength( vector<int> &arr )
  2. {
  3. const auto arr_size = arr.size();
  4. if( arr_size < 2 ) { return arr_size; }
  5. set<int> s;
  6. int maxLen = 0;
  7. for( int left = 0, right = 0; right < arr_size; ++right )
  8. {
  9. // s中有和arr[right]重复的元组,则从left索引开始删除,直到没有重复。
  10. while( s.count( arr[right] ) ) { s.erase( arr[left++] ); }
  11. s.insert( arr[right] );
  12. maxLen = std::max( maxLen, right - left + 1 );
  13. }
  14. return maxLen;
  15. }

方法二:数组保存不重复串

  1. int maxLength(vector<int>& arr) {
  2. const auto arr_size = arr.size();
  3. if(arr_size < 2) return arr_size;
  4. int hit[100000] = {0}; // 题目说明了数据规模小于10^5。
  5. int maxLen = 0;
  6. for(int left = 0, right = 0; right < arr_size;)
  7. {
  8. if(!hit[arr[right]])
  9. {
  10. hit[arr[right]] = 1;
  11. maxLen = max(maxLen, right++ - left + 1);
  12. }
  13. else hit[arr[left++]] = 0;
  14. }
  15. return maxLen;
  16. }

情形二:字符类型元素

方法一:set集合

  1. int lengthOfLongestSubstring(string s) {
  2. set<int> dataSet;
  3. const auto size = s.size();
  4. int maxLen = 0;
  5. for( int left = 0, right = 0; right < size; ++right )
  6. {
  7. while( dataSet.count( s[right] ) ) { dataSet.erase( s[left++] ); }
  8. dataSet.insert( s[right] );
  9. maxLen = std::max( maxLen, right - left + 1 );
  10. }
  11. return maxLen;
  12. }

方法二:数组

  1. int lengthOfLongestSubstring(string s) {
  2. char dataSet[1 << ( 1 << 3 * sizeof( char ) )] = {0};
  3. const auto size = s.size();
  4. int maxLen = 0;
  5. for( int left = 0, right = 0; right < size; ++right )
  6. {
  7. while( dataSet[s[right]] ) { dataSet[s[left++]] = 0; }
  8. dataSet[s[right]] = 1;
  9. maxLen = std::max( maxLen, right - left + 1 );
  10. }
  11. return maxLen;
  12. }

训练题:Z字形变换

题目:http://t.cn/Ai91zWfp

  1. // 字符串:PAYPALISHIRING,行数为3时
  2. //
  3. // 0 1 2 3 4 5 6
  4. // 0 P A H N
  5. // 1 A P L S I I G
  6. // 2 Y I R
  7. //
  8. // 输出字符串:PAHNAPLSIIGYIR
  9. // 字符串:PAYPALISHIRING,行数为4时
  10. //
  11. // 0 1 2 3 4 5 6
  12. // 0 P I N
  13. // 1 A L S I G
  14. // 2 Y A H R
  15. // 3 P I
  16. //
  17. // 输出字符串PINALSIGYAHRPI
  18. // 行数为5时
  19. // 0 1 2 3 4 5 6
  20. // 0 P I A
  21. // 1 A A A A
  22. // 2 Y A A A
  23. // 3 P A A A
  24. // 4 A A A

方法一:模拟法

  • 时间复杂度:O(n)
  • 空间复杂度:O(n) + O(n),一个是返回的字符串空间,一个是vector的空间。 ```cpp

// 输出字符串PAYPALISHIRING,行数为3,排列如下: // P A H N vector1装这一行,在这里flag变为+1,可以理解为row增加的方向(是+1还是-1) // A P L S I I G vector2装这一行, // Y I R vector3装这一行,在这里flag变为-1, // // 输出字符串的时候拼接这3个vector即可。

string convert(string s, int numRows) { if( numRows == 1 ) { return s; }

  1. const auto size = s.size();
  2. vector<string> datamap( min(numRows, (int)size) );
  3. char flag = -1;
  4. short row = 0;
  5. for( int idx = 0; idx < size; idx++ )
  6. {
  7. datamap[row].append(1, s[idx] );
  8. if( !( idx % ( numRows - 1 ) ) ) { flag = -flag; }
  9. row += flag;
  10. }
  11. string reverseStr;
  12. reverseStr.reserve( size );
  13. for( auto &row : datamap ) { reverseStr.append( row.begin(), row.end() ); }
  14. return reverseStr;

}

  1. <a name="tpVhB"></a>
  2. ## 方法二
  3. - 时间复杂度:O(n)
  4. - 空间复杂度:O(n),为返回的字符串空间。
  5. ```cpp
  6. // 逆向思维,从结果逆向分析。
  7. // -----------------------------------------------
  8. // 字符串:PAYPALISHIRING,行数为4时
  9. // 0 1 2 3 4 5 6
  10. // 0 P I N
  11. // 1 A L S I G
  12. // 2 Y A H R
  13. // 3 P I
  14. // -----------------------------------------------
  15. // 按行,从上到下,从左到右输出,设row为行,idx为row行的第idx个字符。numRows为行数。
  16. // 则:row的区间为[0, numRows-1]
  17. // 我们需要找到源字符串索引oldIdx关于row、idx的表达式即可。
  18. // 分析排列规律我们可以得出:
  19. // row = 0或numRows-1时,oldIdx = ( numRows * 2 - 2 ) * idx + row;
  20. // row = 其他时,我们需要从奇偶索引区分,设idx为第k个奇数/偶数,k从0开始。k = idx/2
  21. // 当idx为奇数时,oldIdx = ( numRows * 2 - 2 )*k + (numRows * 2 - 2) - row
  22. // 当idx为偶数时,oldIdx = ( numRows * 2 - 2 )*k + row
  23. string convert( string s, int numRows )
  24. {
  25. if( numRows == 1 ) { return s; }
  26. const auto size = s.size();
  27. string ret;
  28. ret.reserve( size );
  29. for( short row = 0, oldIdx = -1; row <= numRows - 1; ++row )
  30. {
  31. for( short idx = 0;; ++idx )
  32. {
  33. if( row == 0 || row == numRows - 1 )
  34. {
  35. oldIdx = ( numRows * 2 - 2 ) * idx + row;
  36. }
  37. else
  38. {
  39. oldIdx = ( numRows * 2 - 2 ) * ( idx / 2 ) + ( ( idx & 0x1 ) ? ( numRows * 2 - 2 - row ) : row );
  40. }
  41. if( oldIdx < size )
  42. {
  43. ret.append( 1, s[oldIdx] );
  44. }
  45. else { break; }
  46. }
  47. }
  48. return ret;
  49. }

训练题:翻转单词顺序

题目:http://t.cn/A6qdlYWK

方法一:模拟法

  1. string reverseWords(string s) {
  2. if(s.empty()) return "";
  3. string ret;
  4. const auto size = s.size();
  5. ret.reserve(s.size());
  6. // idx:从尾到头遍历字符串
  7. // left,right为搜索到的单词索引范围
  8. for(int idx = size - 1, left, right; idx >= 0; --idx){
  9. // 找到第一个非空字符,也即是单词的结尾字母索引。
  10. for(; idx >= 0 && s[idx] == ' '; --idx);
  11. // 没有找到,直接结束
  12. if(idx < 0) break;
  13. right = idx;
  14. // 找到这个单词的头部索引
  15. for(; idx > 0 && s[idx - 1] != ' '; --idx);
  16. left = idx;
  17. // 追加
  18. ret.append(s.begin() + left, s.begin() + right + 1).append(" ");
  19. }
  20. // 最后结尾会多一个空格。
  21. ret.pop_back();
  22. return ret;
  23. }

方法二:栈

  • 时间复杂度:O(n)
    • 新开辟一个string
  • 空间复杂度:O(n) ```cpp

string reverseWords( string s ) { stack stk; // 从栈底往上,每两个元素表示一个单词的起始/结束索引(从前往后数的单词) const auto inputSize = s.size(); // 输入的字符串长度 int newLen = 0; // 新的字符串的长度

  1. // 遍历一遍字符串,统计出所有单词起始/结束索引,保存到stk栈中。
  2. for( int on = 0, head = 0; head < inputSize; ++head )
  3. {
  4. if( on && s[head] == ' ' ) // 遍历到第一个空格字符且前一次遍历的是非空格字符,则表示到了单词的结尾,将索引压栈
  5. {
  6. newLen += head - stk.top(); // 将该单词的长度统计到newLen中。
  7. stk.push( head - 1 ); // 将单词结束索引压栈
  8. on = 0; // head当前并不在单词的索引中,即head所指的是空格索引
  9. }
  10. else if( !on && s[head] != ' ' ) // 遍历到第一个非空格字符,即将索引压栈,表示当前单词的起始索引。
  11. {
  12. stk.push( head ); // 将单词起始索引压栈
  13. on = 1; // 此后遍历的非空格字符并不是第一个,即只是单词的中间索引
  14. }
  15. if( head == inputSize - 1 && on ) // 特殊情况,当正在遍历一个单词,且刚好又到了结尾时。
  16. {
  17. // lastIdx表示这个单词的结尾索引,有两种情况:
  18. // 情况一:"...abcd ",结尾索引是head-1
  19. // 情况二:"...abcd",结尾索引是head
  20. int lastIdx = s[head] == ' ' ? head - 1 : head;
  21. newLen += lastIdx - stk.top() + 1;
  22. stk.push( lastIdx );
  23. }
  24. }
  25. if( !newLen ) { return ""; } // 没有统计到单词,直接返回空。
  26. string reverseStr;
  27. reverseStr.reserve( newLen + ( stk.size() >> 1 ) - 1 ); // 设置好新字符串预计长度(加上空格数),避免发生“内存重组”
  28. // start,end表示一个单词的起始结尾索引。
  29. for( int start = -1, end = -1, newStart = 0; !stk.empty(); )
  30. {
  31. end = stk.top();
  32. stk.pop();
  33. start = stk.top();
  34. stk.pop();
  35. reverseStr.append( s.begin() + start, s.begin() + end + 1 ); // 将单词拼接到reverseStr结尾
  36. reverseStr.append( " " ); // 别忘了加上空格
  37. }
  38. reverseStr.pop_back(); // 最后一个单词结尾不需要空格
  39. return reverseStr;

}

  1. <a name="vaek3"></a>
  2. ## 方法三:stringstream+栈
  3. - 时间复杂度:O(n)
  4. - 空间复杂度:O(n) + O(n)
  5. 和上方法的差别是,本方法借助stringstream从字符串中直接提取字符串,逻辑上简单了不少,但是效率大大减低,因为有多次string拷贝。
  6. ```cpp
  7. string reverseWords( string s )
  8. {
  9. int newLen = 0; // 新的字符串的长度
  10. stringstream ss( s );
  11. string tmp;
  12. stack<string> stk; // 每个栈元素都意味着发生了一个内存分配以及拷贝,效率会大大降低。
  13. while( ss >> tmp )
  14. {
  15. newLen += tmp.size();
  16. stk.push( tmp );
  17. }
  18. if( !newLen ) { return ""; }
  19. string retStr;
  20. retStr.reserve( newLen + stk.size() - 1 );
  21. while( !stk.empty() )
  22. {
  23. retStr.append( stk.top() );
  24. retStr.append( " " );
  25. stk.pop();
  26. }
  27. retStr.pop_back();
  28. return retStr;
  29. }

训练题:字符串转整数

题目:http://t.cn/A6ZjeVHH

  1. class Solution {
  2. public:
  3. int myAtoi(string s) {
  4. if(s.empty()) return 0;
  5. const auto size = s.size();
  6. int start = 0;
  7. int ret = 0;
  8. char tmp = 0;
  9. bool positive = true;
  10. for(; start < size && s[start] == ' '; ++start);
  11. if(start == size) return 0;
  12. if(s[start] == '+' && ++start);
  13. else if(s[start] == '-' && ++start) positive = false;
  14. if(start == size) return 0;
  15. for(; start < size && s[start] >= '0' && s[start] <= '9'; ++start){
  16. tmp = s[start] - '0';
  17. if(ret > 214748364) return positive ? 2147483647 : -2147483648;
  18. if(ret == 214748364 ){
  19. if(positive && tmp >= 7) return 2147483647;
  20. else if(!positive && tmp >= 8) return -2147483648;
  21. }
  22. ret = ret * 10 + tmp;
  23. }
  24. return positive ? ret : ret*-1;
  25. }
  26. };