【问题标题】:An efficient way that deletes specified characters from a string从字符串中删除指定字符的有效方法
【发布时间】:2011-01-11 09:34:42
【问题描述】:

例如,给定一个 str 为“Stackoverflow is for each one”并删除“aeiou”, 该函数应将 str 转换为“Stckvrflw s fr vry n”。

我有一个 char 字符串数组:str[] 和一个要删除的 char 字符数组:remove[]

我的解决方案:循环 str[] 查找 remove[] 中的每个 in 字符。每次将 str[] 左移一位。我相信更好的破解是可能的。

【问题讨论】:

  • 更具体一点。将您正在寻找的时间/空间复杂度添加到您的问题中。如果我们的解决方案适用于短字符串,我会说 O(n^2) 是可以的。
  • 无解(A):告诉面试官改用Python和string.translate(None, "aeiou")。 (B):告诉面试官切换到 Perl 和$string =~ s/[^aeiou]//g
  • 这是我的推理:一些最好的字符串匹配算法是Theta(n)。你有m 这样的模式要检查(特殊情况:模式长度== 1)。所以基本上,您正在查看 Theta(mn) 运行时。
  • @dirkgently:可以在 Theta(m + n) 中完成,请参阅下面的第三个解决方案。
  • @Tarydon:我故意跳过的另一件事是我之前评论中的预处理时间:)

标签: c algorithm data-structures


【解决方案1】:

这是我的版本,if 语句已从复制循环中删除:

#include <stdio.h>
#include <string.h>

int main( void ){
  unsigned char str[]    = "Stackoverflow is for every one";
  unsigned char remove[] = "aeiou";

  unsigned char table[256] = { [ 0 ... 255 ] = 1 };
  for( unsigned char *r=remove; *r; r++ ){ table[*r]=0; }

  unsigned char *source=str, *dest=str;
  while( (*dest = *source++) ) dest += table[*dest];

  printf( "str: '%s'\n", str );
}

【讨论】:

  • +1。数组初始化使用您应该提到的 GCC 扩展。
  • 哇——特定于编译器的扩展?这是作弊!
  • 只有while循环是真实代码,其他行是伪代码。
【解决方案2】:

我相信这是那些“经典”谜题之一。

本质上,您扫描“匹配”字符串并制作可能匹配的查找位表。

然后你遍历 'src' 一次,对照你的表检查每个字符。

O(n) 时间。

算法是这样的:

   static char bits[32];  // Not thread-safe, but avoids extra stack allocation
   char * dest = src;
   memset(bits, sizeof(bits), 0);  
   for (; *remove; remove++)
   {
      bitfields[*match >> 3] |= *remove & 7;
   }

   for (;*src; src++) 
   {
      if (!((bits[*src >> 3] & (*src & 7)) == (*src & 7)))
      { 
        *dest++ = *src;
      }
   }

我相信 ischr()、isdigit()、isspace() 等与这种技术的工作方式类似,但它们的查找表是不变的。

【讨论】:

  • 为了了解正在做的事情,没有那么简洁的方式来表达这一点,但我很高兴看到 有人 明白了。
【解决方案3】:

将整个字符串向左移动一个位置将使这成为一个有效的 O(n^2) 算法。您可以在线性时间内就地执行此操作:

void Remove (char * src, const char * match) {
   char * dest = src;
   for (;;) { 
      char ch = *src++; 
      if (!strchr (match, ch)) *dest++ = ch;  // Copy chars that don't match
      if (!ch) break;                         // Stop when we copy over a null  
   }
}

我在这里假设这些是空终止的。如果不是这种情况,那么您还必须传入长度并适当地修改算法。特别是,您将无法使用 strchr。为了完整起见,这里有一个适用于 char 数组的版本(不是以 null 结尾的)。

// Removes from str[] (of length strlen), all chars that are found
// in match[] (of length matchlen). Modifies str in place, and returns
// the updated (shortened) length of str. 
int Remove (char[] str, int srclen, char[] match, int matchlen) {
   int dst = 0, found;
   for (int src = 0; src < srclen; src++) { 
      char ch = str[src];  
      found = 0;           // Search if this char is found in match
      for (int i = 0; i < matchlen && !found; i++) 
         if (match[i] == ch) found = 1;
      if (!found) str[dst++] = ch;
   }
   return dst;
}

最后,我猜这与我们将要得到的 O(n) 一样接近。我在这里假设 8 位字符并构建一个查找表,所以这应该在 O(n) + O(m) 中运行,其中 m 是匹配字符串的长度。

int Remove (char* str, int srclen, char* match, int matchlen) {
   bool found[256];
   for (int i = 0; i < 256; i++) found[i] = 0;
   for (int i = 0; i < matchlen; i++) found[match[i]] = 1; 

   int dst = 0;
   for (int src = 0; src < srclen; src++) { 
      char ch = str[src];  
      if (!found[ch]) str[dst++] = ch;
   }
   return dst;
}

【讨论】:

  • 你的版本是 O(n^2)。查找strchr 实现。这是一个常见的错误。
  • 很好的解决方案,但线性时间的要求取决于输入 - 如果 strlen(match) == strlen(src),这也是 O(n^2)。
  • @dirkgently,JBRWilkinson。你们俩都是对的。我的算法也是 O(n^2)。可能不能在更少的时间内完成?
  • +1:使用 2 个指针是避免不必要移位的好方法。 @dirkgently,不是 O(n^2) 而是 O(nstrlen(match)) 或 O(nm)。
  • @Nick D:当然可以——正如我在其他评论(对 OP)中指出的那样。我的意思是使用strchr 会给我们一种线性时间复杂度的错误印象。
【解决方案4】:

使用正则表达式查找和替换是一种更紧凑的解决方案。使用 GNU C 库或找到另一个支持正则表达式搜索和替换的库。当然,如果字符每次都不同,则必须在运行时创建正则表达式。如果您坚持当前的方法,请将其拆分为函数。

我也喜欢 Tarydon 的方法。它的工作量更少!

【讨论】:

  • 在这个问题上使用像正则表达式一样通用和强大的工具无疑是可行的,你也许可以用一段简短而优雅的代码来完成它,但它不会在快速和需要很少的额外空间的意义上,效率并不高。
  • 同意。我的回答是基于 homeWorkBoy 刚刚提到了一个更好的 hack 的事实。他没有具体说明他是否想要更快的算法。回想起来,提供更好的算法显然是更好的答案。
【解决方案5】:

如果您能负担得起多一个缓冲区,您可以: 循环 str[] 查找 remove[] 中的每个 in 字符,但不是 shift,而是复制到新数组。

【讨论】:

  • 不需要第二个缓冲区...新字符串(以及其中的每个子字符串)保证不比旧字符串长,因此您可以使用当前字符串的前面缓冲区作为接收缓冲区。可怕且容易搞砸,但是 O(n*m) 时间和 O(1) 额外空间。
【解决方案6】:

我会循环 str[] 并将 remove[] 中不存在的每个字符存储在一个新数组中(比如说 new_str[])。然后用 str[] 交换 new_str[]。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-10
    • 2015-08-16
    • 2015-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多