我有一个长度为n
的字符串s
。在范围i..j
内查找最频繁的字符,你需要使用哪种最有效的数据结构/算法?
该字符串不会随时间变化,我只需要重复查询并询问s[i]
,s[i + 1]
,...,s[j]
中最频繁的字符。
我有一个长度为n
的字符串s
。在范围i..j
内查找最频繁的字符,你需要使用哪种最有效的数据结构/算法?
该字符串不会随时间变化,我只需要重复查询并询问s[i]
,s[i + 1]
,...,s[j]
中最频繁的字符。
一个数组,其中包含每个字符出现的次数。在遍历字符串时,您可以增加相应的值。在执行此操作的同时,您可以记住数组中的当前最大值;或者在最后查找数组中的最高值。
伪代码
arr = [0]
for ( char in string )
arr[char]++
mostFrequent = highest(arr)
如果您希望在区间上获得高效的结果,可以在序列的每个索引处构建一个积分分布向量。然后通过从j + 1和i处减去积分分布,可以获得从s [i],s [i + 1],...,s [j]的区间分布。
以下是Python中的一些伪代码。我假设您的字符是chars,因此有256个分布条目。
def buildIntegralDistributions(s):
IDs=[] # integral distribution
D=[0]*256
IDs.append(D[:])
for x in s:
D[ord(x)]+=1
IDs.append(D[:])
return IDs
def getIntervalDistribution(IDs, i,j):
D=[0]*256
for k in range(256):
D[k]=IDs[j][k]-IDs[i][k]
return D
s='abababbbb'
IDs=buildIntegralDistributions(s)
Dij=getIntervalDistribution(IDs, 2,4)
>>> s[2:4]
'ab'
>>> Dij[ord('a')] # how many 'a'-s in s[2:4]?
1
>>> Dij[ord('b')] # how many 'b'-s in s[2:4]?
1
在数组上执行单次迭代,并针对每个位置记住该位置之前每个字符出现的次数。代码示例如下:
"abcdabc"
对于索引0:
count['a'] = 1
count['b'] = 0
etc...
对于索引1:
....
count['a'] = 1
count['b'] = 1
count['c'] = 0
etc...
对于索引2:
....
count['a'] = 1
count['b'] = 1
count['c'] = 1
....
等等,对于索引 6:
....
count['a'] = 2
count['b'] = 2
count['c'] = 2
count['d'] = 1
... all others are 0
count[j] - count[i-1]
(注意当=0时要小心!)。您需要根据空间和时间复杂度来指定算法要求。
如果您坚持使用O(1)
的空间复杂度,只需排序(例如,如果没有自然比较运算符,则使用位的词典序排序)并计算最高元素的出现次数即可获得O(N log N)
的时间复杂度。
如果您坚持使用O(N)
的时间复杂度,请使用@Luchian Grigore的解决方案,该解决方案还需要O(N)
的空间复杂度(对于K
字母表来说是O(K)
)。
string="something"
arrCount[string.length()];
每次调用 freq() 时访问字符串
freq(char accessedChar){
arrCount[string.indexOf(x)]+=1
}
要获取最常见的字符,请调用 string.charAt(arrCount.max())
struct occurences{
char c;
std::list<int> positions;
};
为每个字符保留一个std::list<occurences>
,以便快速搜索,您可以保持positions
有序。
如果要最小化内存占用,只需保留递增整数并循环遍历i
.. j
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
}
现在,这仅适用于整个字符串。如果您想重复处理相对较小的子字符串,则应改用第一个版本。否则,我认为您最好的选择可能是像第二个解决方案那样将字符串分成可管理的块;这样,您可以从缓存中获取大部分信息,只需在迭代器所在的块中重新计算频率。
最快的方法是使用unordered_map
或类似的数据结构:
pair<char, int> fast(const string& s) {
unordered_map<char, int> result;
for(const auto i : s) ++result[i];
return *max_element(cbegin(result), cend(result), [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
}
在编程中,如果要求最轻量级的内存占用,需要一个非常量输入,可以进行排序,这样就可以使用find_first_not_of
或类似的函数:
pair<char, int> light(string& s) {
pair<char, int> result;
int start = 0;
sort(begin(s), end(s));
for(auto finish = s.find_first_not_of(s.front()); finish != string::npos; start = finish, finish = s.find_first_not_of(s[start], start)) if(const int second = finish - start; second > result.second) result = make_pair(s[start], second);
if(const int second = size(s) - start; second > result.second) result = make_pair(s[start], second);
return result;
}