C++ 如何在std::vector中查找重复项<;字符串>;并返回他们的列表?

C++ 如何在std::vector中查找重复项<;字符串>;并返回他们的列表?,c++,stl,functor,C++,Stl,Functor,如果我有一个向量,比如: Vec1 = "words", "words", "are", "fun", "fun" 结果列表:“乐趣”、“词语” 我试图确定哪些单词是重复的,并返回它们的1个副本的字母顺序向量。我的问题是,我甚至不知道从哪里开始,我发现唯一接近它的东西是std::unique\u copy,它不能完全满足我的需要。具体来说,我输入的是std::vector,但输出的是std::list。如果需要,我可以用函子 至少有人能把我推向正确的方向吗?我已经试过阅读stl文档,但我现在只

如果我有一个向量,比如:

Vec1 = "words", "words", "are", "fun", "fun"
结果列表:“乐趣”、“词语”

我试图确定哪些单词是重复的,并返回它们的1个副本的字母顺序向量。我的问题是,我甚至不知道从哪里开始,我发现唯一接近它的东西是
std::unique\u copy
,它不能完全满足我的需要。具体来说,我输入的是
std::vector
,但输出的是
std::list
。如果需要,我可以用函子


至少有人能把我推向正确的方向吗?我已经试过阅读stl文档,但我现在只是“大脑”被阻塞了

您可以使用std::map来计算出现次数,然后依靠std::list::sort对生成的单词列表进行排序,从而得到一个非常干净的实现。例如:

std::list<std::string> duplicateWordList(const std::vector<std::string>& words) {
    std::map<std::string, int> temp;
    std::list<std::string> ret;
    for (std::vector<std::string>::const_iterator iter = words.begin(); iter != words.end(); ++iter) {
        temp[*iter] += 1;
        // only add the word to our return list on the second copy
        // (first copy doesn't count, third and later copies have already been handled)
        if (temp[*iter] == 2) {
            ret.push_back(*iter);
        }
    }
    ret.sort();
    return ret;
}
std::list duplicateWordList(const std::vector&words){
标准:映射温度;
std::列表ret;
for(std::vector::const_iterator iter=words.begin();iter!=words.end();++iter){
温度[*iter]+=1;
//只需在第二份上的退货清单中添加该词
//(第一份不算,第三份及以后的副本已经处理完毕)
如果(温度[*iter]==2){
反向推送(*iter);
}
}
ret.sort();
返回ret;
}
在那里使用std::map似乎有点浪费,但它完成了任务

  • 创建一个空的
    std::无序集
  • 迭代向量,检查每个项是否为集合的成员
  • 如果它已经在集合中,则这是重复的,因此添加到结果列表中
  • 否则,添加到集合中

  • 由于您希望每个副本只在结果中列出一次,因此您也可以使用哈希集(而不是列表)作为结果。

    在我看来,Ben Voigt从一个很好的基本想法开始,但我要提醒您不要将他的措辞过于字面化

    特别是,我不喜欢在集合中搜索字符串,如果它不存在则将其添加到集合中,如果它存在则将其添加到输出中。这基本上意味着每次我们遇到一个新词时,我们都会搜索我们现有的词集两次,一次是检查一个词是否存在,另一次是插入它,因为它不存在。大部分搜索本质上是相同的——除非其他线程在此期间改变结构(这可能会给出竞争条件)

    相反,我会先尝试将它添加到您看到的一组单词中。它返回一个
    bool
    设置为
    true
    ,当且仅当插入了值,即之前不存在时。这使我们可以将搜索现有字符串和插入新字符串合并为一个插入:

    while (input >> word)
        if (!(existing.insert(word)).second)
            output.insert(word);
    
    这也足够清理流,可以很容易地将测试转换为一个函子,然后我们可以将它与
    std::remove\u copy\u一起使用,如果
    直接生成结果:

    #include <set>
    #include <iterator>
    #include <algorithm>
    #include <string>
    #include <vector>
    #include <iostream>
    
    class show_copies {
        std::set<std::string> existing;
    public:
        bool operator()(std::string const &in) {
            return existing.insert(in).second;
        }
    };
    
    int main() {
        std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
        std::set<std::string> result;
    
        std::remove_copy_if(words.begin(), words.end(),
            std::inserter(result, result.end()), show_copies());
    
        for (auto const &s : result)
            std::cout << s << "\n";
    }
    
    这稍微复杂一点(长一整行!),但当/如果字数非常大时,速度可能会大大加快。还要注意,我使用的std::unique_copy主要是为了产生可见的输出。如果您只想在集合中获得结果,可以使用标准的unique/erase惯用法在
    intermediate

    中获得3行中的唯一项(不包括向量和列表创建,也不包括可读性名称中多余的换行):

    向量向量{“单词”、“单词”、“是”、“有趣”、“有趣”}; 列表输出; 排序(vec.begin(),vec.end()); 设置uvec(vec.begin(),vec.end()); 设置差异(向量开始(),向量结束(), uvec.begin(),uvec.end(), 背面插入器(输出); 编辑 对解决方案的解释:

  • 需要对向量进行排序,以便稍后使用
    set_difference()

  • uvec
    集将自动对元素进行排序,并消除重复项

  • 输出
    列表将由
    vec-uvec
    的元素填充


  • 这里有一个比其他人提出的更好的算法:

    #include <algorithm>
    #include <vector>
    
    template<class It> It unique2(It const begin, It const end)
    {
        It i = begin;
        if (i != end)
        {
            It j = i;
            for (++j; j != end; ++j)
            {
                if (*i != *j)
                { using std::swap; swap(*++i, *j); }
            }
            ++i;
        }
        return i;
    }
    int main()
    {
        std::vector<std::string> v;
        v.push_back("words");
        v.push_back("words");
        v.push_back("are");
        v.push_back("fun");
        v.push_back("words");
        v.push_back("fun");
        v.push_back("fun");
        std::sort(v.begin(), v.end());
        v.erase(v.begin(), unique2(v.begin(), v.end()));
        std::sort(v.begin(), v.end());
        v.erase(unique2(v.begin(), v.end()), v.end());
    }
    
    #包括
    #包括
    模板It unique2(It常量开始,It常量结束)
    {
    它i=开始;
    如果(i!=结束)
    {
    它j=i;
    对于(++j;j!=end;++j)
    {
    如果(*i!=*j)
    {使用std::swap;swap(*++i,*j);}
    }
    ++一,;
    }
    返回i;
    }
    int main()
    {
    std::向量v;
    v、 推回(“文字”);
    v、 推回(“文字”);
    v、 推回(“are”);
    v、 推回(“乐趣”);
    v、 推回(“文字”);
    v、 推回(“乐趣”);
    v、 推回(“乐趣”);
    排序(v.begin(),v.end());
    v、 擦除(v.begin(),unique2(v.begin(),v.end());
    排序(v.begin(),v.end());
    v、 擦除(unique2(v.begin()、v.end()、v.end());
    }
    

    它更好,因为它只需要代码>交换< /COD>没有辅助<代码>向量< /代码>用于存储,这意味着它对于C++的早期版本将是最佳的,并且它不需要元素是可复制的。 如果你更聪明,我想你也可以避免对向量进行两次排序。

    就地(无额外存储)。没有字符串复制(除了到结果列表)。一次排序+一次传递:

    #include <string>
    #include <vector>
    #include <list>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int main() {
            vector<string> vec{"words", "words", "are", "fun", "fun"};
            list<string> dup;
    
            sort(vec.begin(), vec.end());
    
            const string  empty{""};
            const string* prev_p = &empty;
    
            for(const string& s: vec) {
                    if (*prev_p==s) dup.push_back(s);
                    prev_p = &s;
            }
    
            for(auto& w: dup) cout << w << ' '; 
            cout << '\n';
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间std;
    int main(){
    向量向量{“单词”、“单词”、“是”、“有趣”、“有趣”};
    列出dup;
    排序(vec.begin(),vec.end());
    常量字符串为空{“”};
    常量字符串*prev_p=&empty;
    for(常量字符串&s:vec){
    如果(*prev_p==s)重复向后推;
    上一个p=&s;
    }
    
    for(auto&w:dup)cout如果您使用
    std::set
    来存储单词,而不是向量,那么您可以免费获得唯一性和排序。既然您希望它按字母顺序排列,我想您不会介意它是否被排序?
    #include <algorithm>
    #include <vector>
    
    template<class It> It unique2(It const begin, It const end)
    {
        It i = begin;
        if (i != end)
        {
            It j = i;
            for (++j; j != end; ++j)
            {
                if (*i != *j)
                { using std::swap; swap(*++i, *j); }
            }
            ++i;
        }
        return i;
    }
    int main()
    {
        std::vector<std::string> v;
        v.push_back("words");
        v.push_back("words");
        v.push_back("are");
        v.push_back("fun");
        v.push_back("words");
        v.push_back("fun");
        v.push_back("fun");
        std::sort(v.begin(), v.end());
        v.erase(v.begin(), unique2(v.begin(), v.end()));
        std::sort(v.begin(), v.end());
        v.erase(unique2(v.begin(), v.end()), v.end());
    }
    
    #include <string>
    #include <vector>
    #include <list>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int main() {
            vector<string> vec{"words", "words", "are", "fun", "fun"};
            list<string> dup;
    
            sort(vec.begin(), vec.end());
    
            const string  empty{""};
            const string* prev_p = &empty;
    
            for(const string& s: vec) {
                    if (*prev_p==s) dup.push_back(s);
                    prev_p = &s;
            }
    
            for(auto& w: dup) cout << w << ' '; 
            cout << '\n';
    }