String erase() 给出核心转储

String erase() giving core dumped

我正在做代码战争的套路,我需要这样的东西:
当字符串中的字母出现两次时 - 将两者都删除。

我做过这样的事情:

    std::string str2 = str;

for(int i=0;i<str2.size();i++){
    for(int j=0;j<str2.size();j++){

        if(std::tolower(str2[i]) == std::tolower(str2[j]) && j != i){
            n++;
            str2.erase(str2.begin() + i);
            str2.erase(str2.begin() + j);

            i--;
            j--;
        }

    }
}

并被核心转储(这是由 str2.erase(str2.begin() + i); 引起的)。我知道这是我的记忆错误,但你能告诉我这有什么问题吗?我分析了一下,没有发现任何问题。

您正在从 str2 中删除两个字符。如果 i 小于 j,这可能会导致第二次擦除访问到字符串末尾之后(即使不是,您也会擦除错误的字符)。

首先,我们可以观察到,当擦除发生时,我们总是 i<j

问题 1

现在,如果您先删除第一个字符,然后删除第二个字符,那么您就是在向移动目标射击:在删除第一个字符后,您需要调整到第二个字符的新位置:

        str2.erase(str2.begin() + i);
        str2.erase(str2.begin() + j-1);  // because former j c[j] is now c[j-1]

另一个同样适用的变体是先删除第二个字符(不改变第一个字符的位置):

        str2.erase(str2.begin() + j);   // because j> i it has no impact on i
        str2.erase(str2.begin() + i);

优化1

无关,但我们也可以利用我们在 i<j 上的推论来优化或循环并保存大量无用的比较:

for(int i=0;i<str2.size();i++) {
    for(int j=i+1;j<str2.size();j++) {
    ...
    }
}

优化 2

另一个问题是当 i 没有递增时,您在内循环中执行 i--。这将产生两个结果,您将重做您已经做过的比较。所以只需删除这个减量,然后用 j=i+1.

重新启动

Online demo

问题 2(留作练习)

您的算法只会删除成对的字母。因此,如果一个字母出现超过 2 次,则只有前两个字母会被删除。所以你需要检查你的算法(注意不要在第一次出现时擦除几次;-))