如果我正确理解您的要求,那么“最小分区数”由原始向量中单个值的最大频率给出。因此,您可以创建一个直方图,然后在其中找到最大条目。 (这与向量的大小成线性关系。)现在创建 m 向量(其中 m 是刚刚确定的最大频率)并分配每个 m 与其中之一相同的值。保证您可以以不会在分区中出现重复的方式分配剩余元素。
在大小为 n 的输入向量 v 的伪代码中:
- 初始化一个空直方图H
- 对于 v 中的每个项目 x:
- 将 H[x] 增加 1,如果没有这样的 bin 存在,则在之前将其初始化为零
-
m ← H 中的最大频率
- 初始化空向量v1, ..., vm
- 对于每个值 x 分别为 H[x] ≥ 0:
请注意,如果向量中的对象具有确定它们是否与它们的 only 数据成员相等的键,则此方法可以正常工作。但是,如果它们有更多需要保留但不参与确定相等性的状态,则可以轻松调整过程以解决这一问题。
- 初始化一个空直方图H
- 对于 v 中的每个项目 x:
- 将 H[key(x)] 加一,如果不存在这样的 bin,则在之前将其初始化为零
-
m ← H 中的最大频率
- 初始化空向量v1, ..., vm
- 对于 v 中的每个值 x:
-
i ← H[key(x)]
- 将 x 附加到 vi
- 将 H[key(x)] 减一
如果您想要一个快速的解决方案,您可以使用std::unordered_map<int, int> 作为您的直方图。
以下是 C++14 中(最终有点过于笼统)实现的样子。
#include <algorithm> // std::max_element
#include <functional> // std::hash, std::equal_to
#include <iterator> // std::iterator_traits
#include <unordered_map> // std::unordered_map
#include <vector> // std::vector
template<typename FwdIterT,
typename ValueT = typename std::iterator_traits<FwdIterT>::value_type,
typename ValueHashT = std::hash<ValueT>,
typename ValueEqCmpT = std::equal_to<ValueT>>
decltype(auto)
min_partition(const FwdIterT begin, const FwdIterT end)
{
std::vector<std::vector<ValueT>> partitions {};
std::unordered_map<ValueT, int, ValueHashT, ValueEqCmpT> histo {};
for (auto iter = begin; iter != end; ++iter)
histo[*iter]++;
const auto cmpfreq = [](const auto& lhs, const auto& rhs){
return lhs.second < rhs.second;
};
const auto maxbin = std::max_element(histo.cbegin(), histo.cend(), cmpfreq);
partitions.resize(maxbin->second);
for (auto iter = begin; iter != end; ++iter)
partitions.at(histo.at(*iter)-- - 1).push_back(*iter);
return partitions;
}
可以这样使用。
#include <iostream> // std::cout
#include <string> // std::string
#include <utility> // std::begin, std::end
int
main(int argc, char * * argv)
{
using std::begin;
using std::end;
for (int i = 1; i < argc; ++i)
{
const std::string text {argv[i]};
const auto partitions = min_partition(begin(text), end(text));
std::cout << "input: " << text << "\n";
std::cout << "output: " << partitions.size() << " partitions\n\n";
for (auto it1 = begin(partitions); it1 != end(partitions); ++it1)
{
std::cout << "[";
for (auto it2 = begin(*it1); it2 != end(*it1); ++it2)
std::cout << (it2 != begin(*it1) ? ", " : "") << *it2;
std::cout << "]\n";
}
if (i != argc - 1)
std::cout << "\n\n";
}
}
如果给定一些众所周知的字符串作为输入,它会产生以下输出。
input: WEWEREARRESTEDAFTERDADATEDEEREGGS
output: 10 partitions
[W, F, A, T, D, R, E, G, S]
[W, S, T, R, A, D, E, G]
[R, T, A, D, E]
[A, R, D, E]
[R, E]
[E]
[E]
[E]
[E]
[E]
input: ALASDADHADAGLASSSALAD
output: 8 partitions
[H, G, S, L, A, D]
[D, L, S, A]
[L, D, A, S]
[S, D, A]
[A]
[A]
[A]
[A]
input: THEQUICKBROWNFOXJUMPSOVERTHESLEAZYDOG
output: 4 partitions
[Q, I, C, K, B, W, N, F, X, J, U, M, P, V, R, T, H, S, L, E, A, Z, Y, D, O, G]
[T, H, U, R, S, O, E]
[O, E]
[E, O]