如何在 C++ 中的字符串向量中找到重复的单词?

Posted

技术标签:

【中文标题】如何在 C++ 中的字符串向量中找到重复的单词?【英文标题】:How can I find repeated words in a vector of strings in C++? 【发布时间】:2020-10-27 09:59:58 【问题描述】:

我有一个std::vector<string>,其中每个元素都是一个单词。我想打印向量没有重复的单词!

我在网上进行了很多搜索,找到了很多材料,但我不能也不想使用哈希映射、迭代器和“高级”(对我而言)的东西。我只能使用纯字符串比较==,因为我还是个初学者。

所以,让 my_vecstd::vector<std::string> 从 std 输入初始化。我的想法是读取所有向量并在找到任何重复的单词后删除它:

  for(int i=0;i<my_vec.size();++i)
    for (int j=i+1;j<my_vec.size();++j)
      if(my_vec[i]==my_vec[j])
        my_vec.erase(my_vec.begin()+j); //remove the component from the vector
      
    
  

我尝试测试std::vector&lt;std::string&gt; my_vec"hey","how","are","you","fine","and","you","fine"

我确实找到了

嘿,你好吗

所以看起来是对的,但是例如如果我写简单的向量std::vector&lt;std::string&gt; my_vec"hello","hello","hello","hello","hello"

我得到

你好你好

问题是每次调用erase 时,维度都会变小,因此我会丢失信息。我该怎么做?

【问题讨论】:

讽刺的是,std::vector::erase 通过返回一个新的迭代器解决了这个问题。您可以通过抵消++j 来模拟这一点。 你真的应该至少使用迭代器,因为这样做会真正解决你的问题,同时以最温和的方式推广你应该长期接受的东西(即迭代器;标准库实际上是由它们生死攸关的)。 换个词的顺序可以吗?还是需要按照它们存储在向量中的顺序打印它们? 更简单的解决方案是创建一个新的无重复向量。 @idclev463035818 是的,如果改变单词的顺序也没关系 【参考方案1】:

对现有代码的极简主义方法。 j 的自动增量最终会破坏您的算法。不要那样做。相反,仅当您不删除元素时才增加它。

for (int i = 0; i < my_vec.size(); ++i) 
    for (int j = i + 1; j < my_vec.size(); )   // NOTE: no ++j
        if (my_vec[i] == my_vec[j]) 
            my_vec.erase(my_vec.begin() + j);
        
        else ++j; // NOTE: moved to else-clause
    

确实如此。

【讨论】:

非常感谢您的回答,这正是我想要的。顺便说一句,我正在尝试跟踪重复次数:我定义了一个长度为 my_vec.size() 的动态数组repts,我在if 中执行repts[i]++。但是,我没有得到正确的结果。有什么问题? @WhozCraig @Vefhug 很难说没有真正看到你是如何做到的。坦率地说,我会为此使用std::unordered_map&lt;std::string, int&gt;,但如果你认真接受并排向量的想法,你可以do it like this 我现在正在尝试使用std::unordered_map&lt;std::string, int&gt;,但是我真的不明白该怎么做,因为我从未使用过它。您将如何使用它完成任务? @Vefhug 有趣的是你应该问。 See example here。该方法的关键是映射中新映射到元素的值初始化;它是零。所以++m[*it] 将仅在第一次插入时为1(因此我们跳过了向量的擦除)。否则,它将 > 1 并且我们 (a) 在映射中维护更新后的计数,并且 (b) 知道我们需要从向量中删除该实例。老实说,除非您希望保留原始向量顺序(您这样做),否则您甚至不需要向量。 我不知道这样,谢谢。 @WhozCraig 你能确认这比std::vector 的方法慢得多吗?我从一个巨大的.txt 文件中读取,最后一种方法的时间大约是旧方法的两倍。【参考方案2】:

您可以存储元素元素索引以擦除,然后在最后消除它。 或者重复循环,直到没有执行擦除。

第一个代码示例:

std::vector<int> index_to_erase();

for(int i=0;i<my_vec.size();++i)
    for (int j=i+1;j<my_vec.size();++j)
      if(my_vec[i]==my_vec[j])
        index_to_erase.push_back(j);
        
      
    
  
//starting the cycle from the last element to the vector of index, in this 
//way the vector of element remains equal for the first n elements
for (int i = index_to_erase.size()-1; i >= 0; i--)
   my_vec.erase(my_vec.begin()+index_to_erase[i]); //remove the component from the vector
 

第二个代码示例:

bool Erase = true;
while(Erase)
  Erase = false;
  for(int i=0;i<my_vec.size();++i)
    for (int j=i+1;j<my_vec.size();++j)
      if(my_vec[i]==my_vec[j])
        my_vec.erase(my_vec.begin()+j); //remove the component from the vector
        Erase = true;
      
    
  

【讨论】:

不,它有效,因为我开始从最后一个索引中删除元素,这确保第一个 n 元素不会更改它们的索引 这很聪明,也许聪明到需要评论或解释一下 first code 中的方法是我尝试的另一种方法,但我停止了,因为我无法仅删除与一组整数对应的值,但我真的很喜欢你从底部开始的方式@ZigRazor @Vefhug index_to_erase 上的循环是从你的向量中删除元素的一段代码。 @ZigRazor 是的,我意识到了,我只是说这是我之前卡住的地方:) 现在非常清楚【参考方案3】:

你为什么不用std::unique

您可以像这样简单地使用它:

std::vector<std::string> v "hello", "hello", "hello", "hello", "hello" ;
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end()); 

注意元素需要排序,因为std::unique 仅适用于连续重复。

如果您不想更改std::vector 的内容,但只有稳定的输出,我推荐其他答案。

【讨论】:

是的,我忘了补充它需要排序。 据我了解,输出应该是稳定的,即排序选项(除非您之后恢复原始顺序) @idclev463035818 啊,我没有从问题中得到答案,但很可能你是对的 @NutCracker 正如 idclev 指出的那样,我只对不重复的打印感兴趣,忽略排序!顺便说一句,我不知道 std::unique,谢谢 :) 原来我错了。其实我更喜欢你不参考我的答案,因为有更好的(包括这个)【参考方案4】:

在循环内从容器中擦除元素有点棘手,因为在擦除索引i 处的元素后,下一个元素(在下一次迭代中)不在索引i+1 处,而是在索引i 处。

阅读有关擦除元素的惯用方式的erase-remove-idiom。但是,如果您只想在屏幕上打印,有一种更简单的方法来修复您的代码:

for(int i=0; i<my_vec.size(); ++i)
   bool unique = true;
   for (int j=0; j<i; ++j)
       if(my_vec[i]==my_vec[j]) 
           unique = false;
           break; 
       
       if (unique) std::cout << my_vec[i];
   

您应该与之前的元素进行比较,而不是检查当前元素之后的元素。否则,当我认为应该是“bar x y”时,“bar x bar y bar”将导致“x x bar”。

最后但同样重要的是,考虑到使用带有索引的传统循环是一种复杂的方式,而使用迭代器或基于范围的循环要简单得多。不要害怕新东西,从长远来看它会更容易使用。

【讨论】:

【参考方案5】:

您可以简单地使用sortunique 的组合,如下所示。

#include <iostream>
#include <algorithm>
#include <vector>

int main() 
    std::vector<std::string> vec"hey","how","are","you","fine","and","you","fine";
    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end() ), vec.end());
    
    for (int i = 0; i < vec.size(); i ++) 
        std::cout << vec[i] << " ";
    
    std::cout << "\n";

    return 0;

【讨论】:

为什么对向量进行排序?我错过了这一点 @Vefhug 没有排序 std::unique 将无法正常工作。 好的,现在我明白了:std::unique 仅适用于连续重复

以上是关于如何在 C++ 中的字符串向量中找到重复的单词?的主要内容,如果未能解决你的问题,请参考以下文章

使用 C++,尝试使用 for 循环和 std::max 查找向量中的最大单词

在向量中存储重复字符串时节省内存?

如何在向量中的每个字符串中只保留唯一的单词

如何在以前读过的行中找到一些单词并在输出中删除它 - C++ 中的读/写字符串

如何在与用户给定前缀匹配的字符串向量中找到第一个单词?

在 C++ 中比较来自向量的字符串时出现分段错误