【问题标题】:Removing specified characters from a string - Efficient methods (time and space complexity)从字符串中删除指定字符 - 高效的方法(时间和空间复杂度)
【发布时间】:2012-01-12 22:06:23
【问题描述】:

问题是:从给定的字符串中删除指定的字符。

Input: The string is "Hello World!" and characters to be deleted are "lor"
Output: "He Wd!"

解决这个问题涉及两个子部分:

  1. 确定是否要删除给定字符
  2. 如果是,则删除该字符

为了解决第一部分,我将要删除的字符读入std::unordered_map,即我解析字符串“lor”并将每个字符插入哈希图中。稍后,当我解析主字符串时,我会查看这个以每个字符为键的 hashmap,如果返回值非零,则从字符串中删除该字符。

问题 1:这是最好的方法吗?

问题 2:哪个更适合这个问题? std::map 还是 std::unordered_map?由于我对订购不感兴趣,因此我使用了unordered_map。但是创建哈希表是否有更高的开销?在这种情况下该怎么办?使用map(平衡树)还是unordered_map(哈希表)?

现在进入下一部分,即从字符串中删除字符。一种方法是删除字符并将数据从该点向后移动一个位置。在最坏的情况下,我们必须删除所有字符,这需要 O(n^2)。

第二种方法是只将所需的字符复制到另一个缓冲区。这将涉及分配足够的内存来保存原始字符串并逐个字符地复制而忽略要删除的那些。虽然这需要额外的内存,但这将是一个 O(n) 操作。

第三种方法,从第 0 个位置开始读写,每次读取时递增源指针,仅在写入时递增目标指针。由于源指针将始终相同或领先于目标指针,因此我可以在同一个缓冲区上进行写入。这节省了内存,也是一个 O(n) 操作。我也在做同样的事情,最后调用resize 来删除额外的不必要的字符?

这是我写的函数:

// str contains the string (Hello World!)
// chars contains the characters to be deleted (lor)
void remove_chars(string& str, const string& chars)
{
    unordered_map<char, int> chars_map;

    for(string::size_type i = 0; i < chars.size(); ++i)
        chars_map[chars[i]] = 1;

    string::size_type i = 0; // source
    string::size_type j = 0; // destination
    while(i < str.size())
    {
        if(chars_map[str[i]] != 0)
            ++i;
        else
        {
            str[j] = str[i];
            ++i;
            ++j;
        }
    }

    str.resize(j);
}

问题 3: 我可以通过哪些不同的方式来改进此功能。还是我们能做到的最好?

谢谢!

【问题讨论】:

  • 每个问题只问一个问题。
  • ¤ Q1Q2:请注意,CHAR_BIT 在大多数实现中 = 8,实际上最差是 16 位。因此,为了提高效率并且每个字符只有一个 char,只需使用数组进行查找。您可以考虑使用std::bitsetstd::vector&lt;bool&gt;Q3:为了可用性,制作一个类似函数的包装器。为了编码清晰,忘记烦恼string::size_type。只需使用intptrdiff_t。干杯,
  • 这个q。最好在代码审查网站上询问。
  • 据我所知,您映射到的唯一值是 1。因此,为什么要使用 unordered_map,而不是 unordered_set

标签: c++ string algorithm


【解决方案1】:

干得好,现在学习标准库算法和提升:

str.erase(std::remove_if(str.begin(), str.end(), boost::is_any_of("lor")), str.end());

【讨论】:

  • 谢谢。棒极了。我不知道 boost:is_any_of。
【解决方案2】:

假设您正在研究算法,并且对库解决方案不感兴趣:

当可能的键数量很大时,哈希表最有价值,但您只需要存储其中的几个。如果您要从数字序列中删除特定的 32 位整数,您的哈希表将是有意义的。但是对于 ASCII 字符,这就有点过分了。

只需创建一个包含 256 个布尔值的数组,并为要删除的字符设置一个标志。每个输入字符只使用一个表查找指令。哈希映射至少涉及一些更多的指令来计算哈希函数。在空间方面,一旦将所有辅助数据加起来,它们可能就不再紧凑了。

void remove_chars(string& str, const string& chars)
{
    // set up the look-up table
    std::vector<bool> discard(256, false);
    for (int i = 0; i < chars.size(); ++i)
    {
        discard[chars[i]] = true;
    }

    for (int j = 0; j < str.size(); ++j)
    {
        if (discard[str[j]])
        {
            // do something, depending on your storage choice
        }
    }
}

关于您的存储选择:根据您是否需要保留输入数据,在选项 2 和 3 之间进行选择。 3 显然是最有效的,但您并不总是需要就地程序。

【讨论】:

  • 我会使用bitset&lt;256&gt;,但仍然是一个很好的答案。比strspnstrcspn 稍微高效一点,因为您只需初始化表一次。
  • bitset 针对空间进行了优化。它将位打包到一个较小的空间中,代价是额外的位掩码操作来存储和检索位。它对于大型位集很有用,但肯定比vector&lt;bool&gt; 慢。 (不是对抗,只是解释我的选择)
  • 我相信vector&lt;bool&gt; 仍然专门等同于下面的bitsetarray&lt;bool, 256&gt; 将是两全其美:没有动态分配和解压大小。
  • @japreiss 感谢您的回答。据我了解, char 不需要无符号。它的值也可以在 -128 到 +127 之间,具体取决于平台。如果是这样的话,discard[chars[i]] 不会导致内存故障吗?
  • @Vinay 没错,我没有想到这一点。由于实际数值无关紧要,您总是可以reinterpret_cast&lt;unsigned char&gt;。这似乎有点hacky,但它会工作。您也可以将256 替换为1 &lt;&lt; (sizeof(unsigned char) - 1),以减少对实现的依赖。
【解决方案3】:

这是一个KISS 解决方案,具有许多优点:

void remove_chars (char *dest, const char *src, const char *excludes)
{
    do {
        if (!strchr (excludes, *src))
            *dest++ = *src;
    } while (*src++);
    *dest = '\000';
}

【讨论】:

    【解决方案4】:

    您可以在strcspnstrspn 之间进行ping pong,以避免需要哈希表:

    void remove_chars(
        const char *input, 
        char *output, 
        const char *characters)
    {
        const char *next_input= input;
        char *next_output= output;
    
        while (*next_input!='\0')
        {
            int copy_length= strspn(next_input, characters);
            memcpy(next_output, next_input, copy_length);
    
            next_output+= copy_length;
    
            next_input+= copy_length;
            next_input+= strcspn(next_input, characters);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 2018-09-11
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多