从字符串中删除指定字符的有效方法

时间:2010-01-31 10:55:56

标签: c algorithm data-structures

例如,给定一个“ Stackoverflow是为每一个 ”并删除“aeiou”, 该函数应将str转换为“ Stckvrflw s fr v n n ”。

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

我的解决方案:循环str []在remove []中查找每个字符。每次都移动str []一个地方。我相信更好的黑客是可能的。

6 个答案:

答案 0 :(得分:5)

将整个字符串向左移动一个位置将有效地使其成为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;
}

答案 1 :(得分:2)

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

实质上,您扫描'match'字符串并创建可能匹配的查找位表。

然后你走过'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()等与此技术类似,但它们的查找表是不变的。

答案 2 :(得分:2)

这是我的版本,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 );
}

答案 3 :(得分:0)

如果你能再买一个缓冲区,你可以: 循环str []在remove []中查找每个字符,但不是shift,而是复制到新数组。

答案 4 :(得分:-1)

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

答案 5 :(得分:-1)

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

我也喜欢Tarydon的做法。它的工作少了!!