如何使用字符串的原始索引来擦除单个字符?

How to use original index of a string to erase single character?

提问人:Sonny 提问时间:7/19/2023 最后编辑:Vlad from MoscowSonny 更新时间:7/19/2023 访问量:149

问:

假设我有一个字符串“abcd”和一个向量 [4,1,3,2] 来索引字符串。例如,vector 的第一个元素是 4,所以我应该删除“abcd”中的第 4 个字符,它指的是“d”。那么 vector 的第二个元素是 1,所以我应该删除“abcd”中的第一个字符,它指的是“a”,依此类推。每次删除一个字符时,我都想记录操作的字符串。这是我的代码:

# include <iostream>
# include <vector>

using namespace std;

int main()
{
    int m;
    cin >> m;
    string T;
    cin >> T;
    cout << T << endl;
    vector<int> arr(m);
    for(auto &x : arr) cin >> x;
    // Remove character from string at given index position
    for (int i = 0; i < m; i++){
        T.erase(T.begin() + arr[i]-1);
        cout << T << endl;
    }
    return 0;
}

但是,我在输出中遇到了一些问题,我该如何解决它?

4
abcd
abcd
4 1 3 2
abc
bc
Segmentation fault
C++ 字符串 字符 stdvector 擦除

评论

1赞 Drew Dormann 7/19/2023
从字符串中删除字符时,映射到旧字符串的索引会发生什么情况?擦除这些字符有什么成就吗?
2赞 lastchance 7/19/2023
尝试使用并行布尔数组标记“擦除”的元素,而不是实际删除它们。不能擦除只有两个字符的字符串的第三个字符。
1赞 463035818_is_not_an_ai 7/19/2023
这是错误的重复。此处没有迭代器失效。
1赞 463035818_is_not_an_ai 7/19/2023
"....所以我应该删除'abcd'中的第一个字符,它指的是'a',...”你的代码没有这样做。在第二次迭代中,您将从 not from 中删除字符abcabcd
1赞 463035818_is_not_an_ai 7/19/2023
@πάνταῥεῖ是的,我已经意识到评论措辞不当。迭代器无效。然而,这不是代码中的问题

答:

3赞 463035818_is_not_an_ai 7/19/2023 #1

将原始字符串中字符的索引与更新字符串中相同字符的索引混淆。

考虑字符串和擦除位置和字符(索引从 0 开始)。然后,首先删除第一个字符并拥有新字符串。现在,您不能再删除索引处的字符,因为没有字符。原始字符串中处于位置的字符现在位于索引处。ab01b1b10

正如评论中提到的,最简单的方法是保持原始字符串不变,这样你就可以使用它的索引,并在一个单独的向量中跟踪“删除的”索引:

std::vector<bool> removed_chars(T.size());
for (size_t i = 0; i < arr.size(); i++){  
     removed_chars[ arr[i] ] = true; // "remove" the next char
     for (size_t j = 0; j < T.size(); j++) {   
           if (! removed_chars[ j ]) std::cout << T[j]; // print the char if it is not removed yet
           std::cout << "\n";
     }
}

请注意,我假设输入使用从 0 开始的索引。如果输入使用从 1 开始的索引,我会在接受输入时尽早更正。

1赞 Vlad from Moscow 7/19/2023 #2

首先,C++ 中的索引从 .因此,如果您遵循此约定会更好。也就是说,带有索引的向量应包含以下序列,而不是 .0{ 3, 0, 2, 1 }{ 4, 1, 3, 2 }

一个简单的方法可以如下所示。要正确确定字符串中的索引,您需要检查字符串中有多少元素已被小于 currect 索引的索引擦除。

这是一个演示程序。

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>

int main()
{
    std::vector<size_t> v = { 3, 0, 2, 1 };
    std::string s( "abcd" );

    for (size_t i = 0; i < v.size(); i++)
    {
        auto n = std::count_if( std::begin( v ), std::next( std::begin( v ), i ),
            [=]( const auto &item ) { return item < v[i]; } );

        s.erase( v[i] - n, 1 );
        std::cout << s << '\n';
    }
}

程序输出为

abc
bc
b
0赞 lastchance 7/19/2023 #3

如果你在相关索引处标记字符串的字符,你几乎可以在没有并行数组的情况下做到这一点。(例如,作为 null 字符。

以下内容留下了从 1 开始的索引(我是 Fortran 程序员!

# include <iostream>
# include <vector>
# include <string>
# include <algorithm>
# include <iterator>
using namespace std;

int main()
{
    int m;
    string T;
    cout << "Enter m: ";   cin >> m;
    cout << "Enter string: ";   cin >> T;
    vector<int> arr(m);
    cout << "Enter " << m << " positions [1-indexed]: ";
    for (auto &x : arr ) cin >> x;
    // MARK character in string at given index position (as the null character, say)
    for (int i = 0; i < m; i++)
    {
       T[arr[i]-1] = 0;
       copy_if( T.begin(), T.end(), ostream_iterator<char>( cout ), [](char &c){ return c; } );
       cout << '\n';
    }
}

跑:

Enter m: 4
Enter string: abcd
Enter 4 positions [1-indexed]: 4 1 3 2
abc
bc
b

实际上,我刚刚发现,如果你用 char(7) 标记它,那么你可以只输出字符串而不是使用 copy_if。不过,不知道这是否是标准行为。

for (int i = 0; i < m; i++)
{
   T[arr[i]-1] = 7;
   cout << T << '\n';
}

评论

0赞 463035818_is_not_an_ai 7/19/2023
arr[i]-1将从 1 开始的索引转换为从 0 开始的索引。问题就在于你在哪里做这件事。我建议在读取输入时执行一次,而不是每次使用索引时都执行一次
0赞 lastchance 7/19/2023
@463035818_is_not_an_ai,从 1 开始和从 0 开始的索引之间的转换是我所知道的最容易出错的编码活动之一(我经常在 Fortran 和 C++ 之间切换)。这不是我喜欢在深夜累的时候做的事情。最好的避免错误的策略可能是非常明确的代码行或类似代码。也许我应该重命名以表示这一点。我将其保留为基于 1 的索引,因为这是 OP 使用的。index=position-1arrpos
0赞 463035818_is_not_an_ai 7/19/2023
我的评论只是关于“以下内容留下了基于 1 的索引”的吹毛求疵。您不会“保留它以 1 为基础的索引”,而是应用转换。你只能把它留到尽可能晚的地方。
0赞 lastchance 7/19/2023
啊,公平的评论。我的意思是“INPUT 假设从 1 开始的索引”。是的,我必须转换为基于 0 的系统(如果我用 C++ 进行)。