6

我有一个std::string. 我想要其中的一组唯一字符,每个字符表示为std::string.

我可以很容易地得到一组字符:

std::string some_string = ...
std::set<char> char_set(some_string.begin(), some_string.end());

我可以将它们转换为这样的字符串:

std::set<std::string> string_set;
for (char c: char_set) {
    string_set.emplace(1, c);
}

但这样的做法似乎很尴尬。有没有更好的(最好是标准库单行)方法来做到这一点?

4

5 回答 5

5

您可以使用:

std::for_each(some_string.begin(), some_string.end(),
              [&string_set] (char c) -> void { string_set.insert(std::string({c}));});

您还可以使用:

   for (char c: some_string)
   {
      string_set.insert(std::string{c});
   }

工作方案:

#include <iostream>
#include <string>
#include <set>
#include <algorithm>

int main()
{
   std::string some_string = "I want the set of unique characters in it";
   std::set<std::string> string_set;
   for (char c: some_string)
   {
      string_set.insert(std::string{c});
   }

   for (std::string const& s: string_set)
   {
      std::cout << s << std::endl;
   }
}

输出:

我
一个
C
e
F
H
一世
n
○
q
r
s
吨
你
w
于 2015-04-21T18:21:45.370 回答
4

Atransform可以用作单线:

transform(begin(some_string), end(some_string),
          inserter(string_set, begin(string_set)),
          [] (char c) -> std::string { return {c}; });

我不建议使用此解决方案,因为它非常难以阅读。通常,您希望编写直观且易于理解的代码。您在答案中所写的内容已经足够了,我不建议您寻找捷径来将您的代码简化为一条线,同时牺牲其清晰度。

于 2015-04-21T18:10:50.330 回答
2

有没有更好的(最好是标准库单行)方法来做到这一点?

不。您在 C++ 标准库中找到的任何内容都适用于更复杂的情况,它们简化了您必须编写的代码。在您的情况下,您的代码更简单。试图强迫自己使用标准库中的一些东西会使你的代码更加复杂。

已经发布了三个答案来证明这一点 - 它们完全符合您的要求,但它们几乎一目了然,并且当编译器无法优化它们时它们会增加不必要的开销。

您的 for 循环是更好的解决方案。它很简单,它向读者传达了意图,并且编译器很容易优化。没有理由浪费时间为一个简单的问题寻找更复杂的解决方案。

所有解决方案都是正确的,但您应该始终选择最简单的正确解决方案。编写更少的代码,而不是更多。

于 2015-04-21T18:36:03.500 回答
0

我怀疑你想要的是一个好主意,如果你真的坚持,你可以创建一个支持从隐式转换char、隐式转换到的类std::string,并且可以与自身的另一个实例或字符串进行比较:

class cvt {
    char val;
public:
    cvt(char val) : val(val) {}

    bool operator<(cvt other) const { return val < other.val; }

    bool operator<(std::string const &s) const {
        return !s.empty() && val < s[0];
    }
    friend bool operator<(std::string const &s, cvt const &c) {
        return !s.empty() && s[0] < c.val;
    }
    operator std::string() const { return std::string(1, val); }
};

有了这个,我们可以创建我们的set<cvt>,但像使用它一样使用它set<std::string>(因为其中的元素可以/将std::string隐式转换为并与 比较std::string):

int main() {
    std::string some_string = "ZABCDECD";

    // Create our (sort of) set<string> from characters in some_string:
    std::set<cvt> char_set(some_string.begin(), some_string.end());

    // An actual set<string> to use with it:    
    std::set<std::string> strings{ "A", "C", "E", "F", "Y" };

    // demonstrate compatibility:
    std::set_intersection(char_set.begin(), char_set.end(), strings.begin(), strings.end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));
}

Live on Coliru.

如果我们在 Godbolt 上查看为此生成的代码,我们会发现它实际上几乎都是语法糖——实际为cvt该类生成的唯一代码是复制一个字节以cvt从 a创建 a 的微小位char,并将 acvt与 a进行比较string。其他一切都已优化不存在。

如果我们确定我们的字符串不会为空,我们可以简化与 and 的比较return val < s[0];return s[0] < val;在这种情况下,它们也会被优化掉,所以使用 using 生成的唯一代码cvt是从源中复制一个字节来构造一个cvt目的。

根据您的想法,这可能符合您的要求。这是相当多的额外输入,但它优化得很好——以至于将 acvt与字符串进行比较可能比将 a 与 a 进行string比较更快string。到目前为止,最大的缺点可能源于质疑您的基本前提,并想知道为什么您不只是编写一个循环并完成它。

于 2015-04-21T18:44:16.520 回答
0
string setToString(const set<char> &s) {
    string str = "";
    std::accumulate(s.begin(), s.end(), str);
    return str;
}

也许这可能有用。

于 2021-12-15T18:22:51.637 回答