从字符串中删除重复字符的函数仅适用于相邻字符

Function to remove repeated characters from a string only partially works with adjacent characters

本文关键字:字符 函数 适用于 删除 字符串      更新时间:2023-10-16

我知道这是一个经常被问到的问题,如果它很愚蠢,我深表歉意,但我正在尝试从文件中的字符串中删除重复的字符并将新字符串放入另一个文件中。这部分进展顺利。

我遇到的主要问题是我的算法删除字符,它仅适用于相同的连续字符,即使这样也只能部分使用。我正在尝试在 for 循环中用户.erase()这样做,但正如我所说,它不起作用。我哪里出错了?

string removeRepeats(string strIn,string &strOut){
    int i;
    int len = strIn.length();
    for(i = 0;i < len; i++){
        if(strIn[i] == strIn[i+1]){
            strIn.erase(i+1,1);
        }
        len = strIn.length();
    }
    return strOut = strIn;
}

这些是示例文件中输入字符串中的字符串:

aaaaaabbccccc
nnnnmmmvvv
rocko 
refrigerate pool 
fungus 

这是它们在程序运行后的结果:

aaabccc                                                                                                                                                 
nnmmvv                                                                                                                                                  
rocko                                                                                                                                                   
refrigerate                                                                                                                                             
pol                                                                                                                                                     
fungus  

你只检查相邻的字符:if(strIn[i] == strIn[i+1]) { ...

您可以更有效地做到这一点,但我想先发表一些评论:

返回

或通过引用传递,但不能同时返回两者

  • 返回strOut并通过引用传递strOut。您应该选择一个或另一个。在我下面写的代码中,我选择返回strOut .

最小化变量的范围

  • 与 C 不同,在 C++ 中,您可以在 for 循环中初始化变量。您还需要尝试最小化循环变量以外的其他变量的范围。在代码中创建一个变量len 。如果您改用for(size_t i = 0; i < strIn.length(); ++i),则无需在 if -语句后更新它。

返回作业简直太奇怪了

  • return strOut = strIn;很奇怪。您不会经常在C++中看到这种情况(请参阅返回按引用传递,但不能同时看到两者(。如果你真的想return strOut,在你改变它之前创建一个strIn的副本并在副本上做所有的字符串突变会更有意义。

以下是我将对您的代码所做的更改(无论算法的正确性如何(:

std::string removeRepeats(std::string strIn){
    std::string strOut = strIn;
    for(size_t i = 0;i < strOut.length(); ++i){
        if(strOut[i] == strOut[i+1]){
            strOut.erase(i+1,1);
        }
    }
    
    return strOut; 
}
你会发现

,这要干净得多。

现在解决您的问题。

由于只有 128 个 ASCII 字符,您可以创建一个布尔数组并检查您以前是否见过某个字符。

因为您想保留最后一个重复字符,所以我们需要有点棘手。下面是将保留第一个重复字符的代码。

C++11 已批准

std::string remove_repeats(std::string input_string) {
  // You have seen no characters yet
  bool seen[128] = { false }; 
  
  std::string output_string = "";
  // for every character in the string
  for(auto c: input_string) {
    // if we haven't seen the the ASCII yet
    if(!seen[128-c]) {
      // append it to our output string
      output_string+=c;
      // mark the letter as seen
      seen[128-c] = true;
    }
  }
   
  return output_string;
}

这是 ideone。

如果不能使用 C++11,可以执行以下操作:

std::string remove_repeats(std::string input_string) {
  // You have seen no characters yet
  bool seen[128] = { false };  
  
  std::string output_string = "";
  // for every character in the string
  for(size_t i = 0; i < input_string.length(); ++i) {
    char c = input_string[i];
    // if we haven't seen the the ASCII yet
    if(!seen[128-c]) {
      // append it to our output string
      output_string+=c;
      // mark the letter as seen
      seen[128-c] = true;
    }
  }
   
  return output_string;
}

这是非 C++11 版本的 ideone。

然而

你想保留最后一个。这就是它变得有趣的地方。

如果我们反转

字符串 (1(,运行我们的算法 (2(,然后重新反转 (3(,我们将得到所需的输出:

(1(《你好世界》->《DLROW Olleh》

(2("DLROW Olleh"->"DLROW EH">

(3("DLROW EH"->"他世界">

具体操作方法如下:

std::string remove_repeats(std::string input_string) {
  // You have seen no characters yet
  bool seen[128] = { false };  
  // Reverse the input string
  std::reverse(input_string.begin(), input_string.end());
  
  std::string output_string = "";
  // for every character in the string
  for(auto c: input_string) {
    // if we haven't seen the the ASCII yet
    if(!seen[128-c]) {
      // append it to our output string
      output_string+=c;
      // mark the letter as seen
      seen[128-c] = true;
    }
  }
  // Reverse the output string
  std::reverse(output_string.begin(), output_string.end());
  return output_string;
}

请务必#include <algorithm> std::reverse

最终工作理念

所以你需要做的是在寻找重复的字符时,你是否在现有的for循环中嵌套了一个嵌套的for循环,所以:

string removeRepeats(string strIn,string &strOut){
int i;
int k;
int len = strIn.length();
for(i = 0;i < len; i++){
    for(k = i+1 ; k < len; k++){
        if(strIn[i] == strIn[k]){
            strIn.erase(k,1);
            k--;
        }
    }
    len = strIn.length();
}
return strOut = strIn;
}

这将修复程序的比较部分。 你的问题在于你的代码只检查每个数字后面紧随其后的数字,而不是所有其他字符