【问题标题】:Interview Question : Trim multiple consecutive spaces from a string面试题:从一个字符串中修剪多个连续的空格
【发布时间】:2011-04-06 03:30:23
【问题描述】:

这是一道面试题 寻找从字符串中修剪多个空格的最佳解决方案。这个操作应该是就地操作。

input  = "I    Like    StackOverflow a      lot"
output = "I Like StackOverflow a lot"

不允许使用字符串函数,因为这是一道面试题。寻找问题的算法解决方案。

【问题讨论】:

  • 你的意思是“in place”而不是“in memory”?
  • @MAK:是的,感谢您指出。

标签: c++ c string


【解决方案1】:

使用<algorithm> 是否符合“算法解决方案”的条件?

#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
struct BothAre
{
    char c;
    BothAre(char r) : c(r) {}
    bool operator()(char l, char r) const
    {
            return r == c && l == c;
    }
};
int main()
{
    std::string str = "I    Like    StackOverflow a      lot";
    std::string::iterator i = unique(str.begin(), str.end(), BothAre(' '));
    std::copy(str.begin(), i, std::ostream_iterator<char>(std::cout, ""));
    std::cout << '\n';
}

试运行:https://ideone.com/ITqxB

【讨论】:

  • @Matthieu:我不会称之为滥用 - unique stl 算法正是为这种情况设计的。诚然,要求函子类很尴尬,但这是 C++0X 之前 stl 算法的一个相当普遍的限制。
  • @Eclipse:我称其为滥用,因为它对于无函子的版本有点牵强,它仅使用==。在这里,仿函数改变了语义,因为比较相等的两个项目不一定合并......虽然我对这个技巧感到非常惊讶,特别是因为仿函数上的一个简单的“语义”改变帮助劫持了算法:)
  • @Matthieu M:这与 std::inner_product 在使用创造性选择的函子(正如 APL 程序员几十年前探索过的那样)调用时所能做的相比无济于事
  • 这是一个C++标准库算法的绝妙使用!
【解决方案2】:

C++0x - 使用 lambda 而不是常规函数对象的解决方案。与 Cubbi 的解决方案进行比较。

#include <string>
#include <algorithm>

int main()
{
    std::string str = "I    Like    StackOverflow a      lot";

    str.erase(std::unique(str.begin(), str.end(),
      [](char a, char b) { return a == ' ' && b == ' '; } ), str.end() );  
}

【讨论】:

  • 而不是a == ' ' 可以使用isspace(a) 更一致。
【解决方案3】:

保留两个索引:下一个可以放入字母的位置(例如,i)和您正在检查的当前索引(例如,j)。

只需使用j 循环遍历所有字符,每当您看到一个字母时,将其复制到索引i,然后递增i。如果您看到前面没有空格的空格,请同时复制该空格。

认为这将在原地工作......

【讨论】:

  • 确保“前面没有空格”检查不只是检查s[j-1] == ' ',因为数组正在被修改。维护一个标记来跟踪您看到的前一个字符是否是空格会更安全。
  • @Kannan:是的,我确实考虑过写这个,但我让读者自己想办法。 ;)
  • @Kannan:在这种情况下实际上不需要这样做。只需检查s[j-1] == ' ' 就足够了。
【解决方案4】:

我会选择这个:

int main(int argc, char* argv[])
{
    char *f, *b, arr[] = "  This        is    a test.                ";
    f = b = arr;

    if (f) do
    {
        while(*f == ' ' && *(f+1) == ' ') f++;
    } while (*b++ = *f++);

    printf("%s", arr);

    return 0;
}

【讨论】:

  • @VJo:想解释一下评论吗?该算法(虽然可能效率低下,因为它复制数组的所有有效元素,无论它们是否必须移动 - 即即使它们留在同一个地方 - 似乎是正确的。
  • @David 是的,它可以正常工作,除了空字符串的情况。你能猜出 arr 包含空字符串时的输出是什么吗?
  • @VJo,它打印一个空字符串。它还会打印什么?
  • @edA 啊,对。如果第一次检查失败,则根本不会执行第二次检查。很抱歉造成混乱
【解决方案5】:

我建议使用一个小型状态机(只是一个简单的 switch 语句)。因为如果面试官和我一样,他们会要求你做的第一个改进是完全修剪任何前导或尾随空格,以便:

"    leading and    trailing    "

转化为:

"leading and trailing"

代替:

" leading and trailing "

这是对状态机设计的一个非常简单的修改,对我来说,通过“直接”编码循环来理解一般的状态机逻辑似乎更容易,即使它需要多几行代码代码而不是直接循环。

如果您认为对直接循环的修改不会太糟糕(可以合理地争论),那么我(作为面试官)会提出我也希望数字中的前导零是修剪。

另一方面,许多面试官实际上可能不喜欢状态机解决方案,因为它是“非最佳”的。我想这取决于您要优化的内容。

【讨论】:

  • 如果您同时实施这两个建议,这将有助于人们与此建议相关联,从而使影响变得切实;-)。
  • 非常好的增强。公平地说,它实际上是被问到的:)。如果您也可以为此提供一些算法,那就太好了...
【解决方案6】:

这里只使用 stdio:

#include <stdio.h>

int main(void){
    char str[] = "I    Like    StackOverflow a      lot";
    int i, j = 0, lastSpace = 0;
    for(i = 0;str[i]; i++){
        if(!lastSpace || str[i] != ' '){
            str[j] = str[i];
            j++;
        }
        lastSpace = (str[i] == ' ');
    }
    str[j] = 0;
    puts(str);
    return 0;
}

【讨论】:

    【解决方案7】:

    修剪多个空格也意味着一个空格应该总是跟一个非空格字符。

    int pack = 0;
    char str[] = "I    Like    StackOverflow a      lot";
    for (int iter = 1; iter < strlen(str); iter++)
    {
        if (str[pack] == ' ' && str[iter] == ' ')
            continue;
        str[++pack] = str[iter];
    }
    str[++pack] = NULL;
    

    【讨论】:

      【解决方案8】:
      int j = 0;
      int k=0;
      char str[] = "I    Like    StackOverflow a      lot"; 
      int length = strlen(str);
      char str2[38];
      for (int i = 0; i < length; i++) 
      { 
          if (str[i] == ' ' && str[i+1] == ' ') 
           continue; 
          str2[j] = str[i];
          j++;
      } 
      
      str2[j] =NULL;
      
      cout<<str2;
      

      【讨论】:

      • 您是否使用以 ' ' 结尾的字符串进行测试?当 i 指向最后一个字符时,str[i+1] 应该会失败
      • 问题要求提供就地版本,这将复制到单独的缓冲区 (str2)。此外,str2 是在堆栈上静态分配的,并且对于某些输入会溢出——这不是一个好主意。而且你已经声明了一个你不使用的变量“k”。
      【解决方案9】:
      void trimspaces(char * str){
          int i = 0;
          while(str[i]!='\0'){
              if(str[i]==' '){
                  for(int j = i + 1; j<strlen(str);j++){
                      if(str[j]!=' '){
                          memmove(str + i + 1, str + j, strlen(str)-j+1);
                          break;
                      }
                  }
              }
              i++;
          }
      }
      

      【讨论】:

        【解决方案10】:

        Haskell 中的函数变体:

        import Data.List (intercalate)
        
        trimSpaces :: String -> String
        trimSpaces =  intercalate " " . words
        

        接下来的算法:

        1. 将字符串分解为单词列表,由空格分隔
        2. 连接列表,在列表中的每个元素之间插入一个空格

        【讨论】:

          【解决方案11】:

          这是删除多余空格的非常简单的实现。

          #include <iostream>
          std::string trimExtraWhiteSpaces(std::string &str);
          int main(){
              std::string str = "  Apple    is a     fruit  and I like     it   .  ";
              str = trimExtraWhiteSpaces(str);
              std::cout<<str;
          }
          std::string trimExtraWhiteSpaces(std::string &str){
              std::string s;
              bool first = true;
              bool space = false;
              std::string::iterator iter;
              for(iter = str.begin(); iter != str.end(); ++iter){
                  if(*iter == ' '){
                      if(first == false){
                          space = true;
                      }
                  }else{
                      if(*iter != ',' && *iter != '.'){
                          if(space){
                              s.push_back(' ');
                          }
                      }
                      s.push_back(*iter);
                      space = false;
                      first = false;
                  }
              }
              return s;
          }
          

          【讨论】:

            猜你喜欢
            • 2011-11-11
            • 2022-09-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-27
            • 1970-01-01
            相关资源
            最近更新 更多