【问题标题】:Transform array of strings into array of ints将字符串数组转换为整数数组
【发布时间】:2019-04-21 07:16:17
【问题描述】:

我有以下字符串数组:

std::string names_str[7] = {"Alex","Louis","Alex","Simon","Matthew", "Carl", "Simon"};

我想创建一个新的整数数组,大小相同,每个索引元素都应该等同于原始字符串数组中的字符串元素。最终结果应如下所示:

int names[7] = {0, 1, 0, 2, 3, 4, 2};

如何实现一种算法,以这种方式用数字填充我的整数数组?

我已经开始使用这样的伪代码,但到目前为止它绝对没有意义:

for (int i = 0; i < 7; i++) {
    int counter = 0;
    if names_str[i] has already been used
        names[i] = assign the number
    else
        names[i] = counter;
    counter++;
}

【问题讨论】:

  • std::setstd::map。这些容器只接受唯一的键(字符串)。
  • 如果你在每次循环中都设置counter = 0;,它就不会算得很远。

标签: c++ arrays transform


【解决方案1】:

您可以使用std::map 来跟踪已知的字符串计数器,例如:

#include <string>
#include <map>

std::string names_str[7] = {"Alex", "Louis", "Alex", "Simon", "Matthew", "Carl", "Simon"};
int names[7];

std::map<std::string, int> counter_map;
int counter = 0;

for (int i = 0; i < 7; ++i)
{
    auto iter = counter_map.find(names_str[i]);
    if (iter == counter_map.end())
        iter = counter_map.insert(std::make_pair(names_str[i], counter++)).first;
    names[i] = iter->second;
}

Live Demo

另外,如果键已经存在,insert() 会向现有键控元素返回 iterator,因此您可以通过 find() 避免冗余搜索:

#include <string>
#include <map>

std::string names_str[7] = {"Alex", "Louis", "Alex", "Simon", "Matthew", "Carl", "Simon"};
int names[7];

std::map<std::string, int> counter_map;
int counter = 0;

for (int i = 0; i < 7; ++i)
{
    auto ret = counter_map.insert(std::make_pair(names_str[i], counter));
    if (ret.second) ++counter;
    names[i] = ret.first->second;
}

Live Demo

无论哪种方式,由于您想将一个数组“转换”为另一个相同大小的数组,这对于std::transform() 来说是一个很好的用例:

#include <string>
#include <map>
#include <algorithm>

std::string names_str[7] = {"Alex", "Louis", "Alex", "Simon", "Matthew", "Carl", "Simon"};
int names[7];

std::map<std::string, int> counter_map;
int counter = 0;

std::transform(std::begin(names_str), std::end(names_str), std::begin(names),
    [&](const std::string &name) {
        auto iter = counter_map.find(name);
        if (iter == counter_map.end())
            iter = counter_map.insert(std::make_pair(name, counter++)).first;
        return iter->second;
    }
);

Live demo

#include <string>
#include <map>
#include <algorithm>

std::string names_str[7] = {"Alex", "Louis", "Alex", "Simon", "Matthew", "Carl", "Simon"};
int names[7];

std::map<std::string, int> counter_map;
int counter = 0;

std::transform(std::begin(names_str), std::end(names_str), std::begin(names),
    [&](const std::string &name) {
        auto ret = counter_map.insert(std::make_pair(name, counter));
        if (ret.second) ++counter;
        return ret.first->second;
    }
);

Live Demo

【讨论】:

  • 发现这也很有趣,但是,在您建议的两个解决方案中都有 names = { 0, 1, 0, 3, 4, 5, 3 }。所以,我们还没有完成。
  • @Micha 你是对的,我误解了 OP 想要的结果。我已经调整了我的示例以产生 OP 提供的所需结果。
  • @RemyLebeau 非常感谢您的努力!将分析所有方法。但是,如果我的数组大小由变量定义,则使用第 4 种解决方案效果很好: int N = 7; std::string names_str[N] = {..names here..};整数名称[N];然后我得到一个错误。 “没有匹配的函数调用'开始...'”。我怎样才能克服这个问题?
  • @weno std::begin()std::end() 适用于标准容器和固定数组,但不适用于可变长度数组(非标准且不可移植)。当需要变长数组时,使用std::vector,例如:std::vector&lt;std::string&gt; names_str(N); /* populate names_str[0..N-1] as needed */ std::vector&lt;int&gt; names(N); /* transform names_str[] into names[] as needed */
【解决方案2】:

@Remy Lebeau,今天学到了一些东西,毫无疑问,但是,也许@weno 没想到会有一个复杂的解决方案(其中几个)。他提出了 C 风格的数组,所以我想知道是否有更 C 风格的解决方案来解决手头的问题。

所以,如果有人感兴趣,这是我的解决方案(添加了一些监控)。

#include <iostream>
#include <string>

int seen(int max,
         std::string const& name,
         std::string const names_str[]
) {
    for(int i = 0; i < max; i++)
        if (name == names_str[i]) 
            return i;

    return -1;
}

int main() {
    std::string names_str[7] = {"Alex", "Louis", "Alex", "Simon", "Matthew",
                                "Carl", "Simon"};
    int names[7]{0};
    int counter {0};

    std::cout << "Monitor:\n";

    for(int i = 0; i < 7; i++) {
        int before {-1};
        if((before = seen(i, names_str[i], names_str)) != -1) { // found!
            std::cout << i << ", " << before << '\n'; // monitor
            names[i] = names[before];
        } else {
            std::cout << i << '\n'; // monitor
            names[i] = counter++;
        }
    }

    std::cout << "Result: \n";

    for(int i = 0; i < 7; i++)
        std::cout << names[i] << '\n';
}

现在就是这样。问候,

【讨论】:

    【解决方案3】:

    很难像 map 提出的解决方案那样优雅。

    但是,这里有一个使用 std::vector 的解决方案,基于 std::stable_sort。
    这个想法只是在排序后检测重复。
    一旦检测到重复,一个简单的循环,类似于 OP 提出的伪代码,就可以得到最终的索引。

    该程序在以后提供。
    可以将此方法适用于类 C 数组。 复杂度为 O(nlogn),因此在我看来,尽管增加了冗长,但它至少与使用 map 提出的解决方案一样有效。
    但是,效率不太可能是此类练习中最重要的标准!

    #include    <iostream>
    #include    <vector>
    #include    <string>
    #include    <algorithm>
    
    int main () {
        std::vector<std::string> names {"Alex","Louis","Alex","Simon","Matthew", "Carl", "Simon"};
        std::vector<int> index (names.size());
        for (int i = 0; i < names.size(); i++) index[i] = i;
    
    //  Sort and duplicate index when repetition
        std::stable_sort (index.begin(), index.end(), [&names] (int i, int j) {return names[i] < names[j];});
        std::vector<bool> seen (names.size(), false);
        std::vector<int> index_corrected (names.size());
        index_corrected[0] = index[0];
        for (int i = 1; i < index.size(); i++) {
            if (names[index[i]] == names[index[i-1]]) {
                seen[index[i]] = true;
                index_corrected[index[i]] = index_corrected[i-1];
            } else {
                index_corrected[index[i]] = index[i];
            }
        }
    
        std::vector<int> index_new (names.size());
        int k = 0;
        for (int i = 0; i < names.size(); i++) {
            if (seen[i]) index_new[i] = index_new[index_corrected[i]];
            else index_new[i] = k++;
        }
        for (auto i: index_new) std::cout << i << " ";
        std::cout << "\n";  
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多