如何在 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_vec
和 std::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<std::string> my_vec"hey","how","are","you","fine","and","you","fine"
我确实找到了
嘿,你好吗
所以看起来是对的,但是例如如果我写简单的向量std::vector<std::string> 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<std::string, int>
,但如果你认真接受并排向量的想法,你可以do it like this
我现在正在尝试使用std::unordered_map<std::string, int>
,但是我真的不明白该怎么做,因为我从未使用过它。您将如何使用它完成任务?
@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 @Vefhugindex_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】:您可以简单地使用sort
和unique
的组合,如下所示。
#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 查找向量中的最大单词