正如所建议的,最省时的算法是将每个字符的频率存储在一个数组中。但是请注意,如果您只是用字符索引数组,您可能会调用未定义的行为。即,如果您正在处理包含 0x00-0x7F 范围之外的代码点的文本,例如使用 UTF-8 编码的文本,则最多可能会出现分段违规,最坏的情况是堆栈数据损坏:
char frequncies [256] = {};
frequencies ['á'] = 9; // Oops. If our implementation represents char using a
// signed eight-bit integer, we just referenced memory
// outside of our array bounds!
正确解决此问题的解决方案如下所示:
template <typename charT>
charT most_frequent (const basic_string <charT>& str)
{
constexpr auto charT_max = numeric_limits <charT>::max ();
constexpr auto charT_min = numeric_limits <charT>::lowest ();
size_t frequencies [charT_max - charT_min + 1] = {};
for (auto c : str)
++frequencies [c - charT_min];
charT most_frequent;
size_t count = 0;
for (charT c = charT_min; c < charT_max; ++c)
if (frequencies [c - charT_min] > count)
{
most_frequent = c;
count = frequencies [c - charT_min];
}
// We have to check charT_max outside of the loop,
// as otherwise it will probably never terminate
if (frequencies [charT_max - charT_min] > count)
return charT_max;
return most_frequent;
}
如果您想多次迭代同一个字符串,请将上述算法(如construct_array)修改为使用std::array <size_t, numeric_limits <charT>::max () - numeric_limits <charT>::lowest () + 1>。然后在第一个 for 循环之后返回该数组而不是最大字符,并省略算法中找到最频繁字符的部分。在您的顶级代码中构造一个 std::map <std::string, std::array <...>> 并将返回的数组存储在其中。然后将查找最常见字符的代码移到该顶级代码中并使用缓存的计数数组:
char most_frequent (string s)
{
static map <string, array <...>> cache;
if (cache.count (s) == 0)
map [s] = construct_array (s);
// find the most frequent character, as above, replacing `frequencies`
// with map [s], then return it
}
现在,这只适用于整个字符串。如果你想重复处理相对较小的子字符串,你应该使用第一个版本。否则,我会说您最好的选择可能是执行第二种解决方案,但将字符串划分为可管理的块;这样,您可以从缓存中获取大部分信息,而只需重新计算迭代器所在的块中的频率。