范围内最常见的角色

时间:2013-01-24 13:50:29

标签: c++ character complexity-theory frequency mode

我有一个长度为s的字符串n。什么是最有效的数据结构/算法用于查找范围i..j中最常见的字符?

字符串不会随着时间的推移而改变,我只需要在s[i]s[i + 1],...,s[j]中重复询问最常见字符的查询。

8 个答案:

答案 0 :(得分:9)

一个数组,其中包含每个字符的出现次数。在迭代字符串一次时增加相应的值。在执行此操作时,您可以记住数组中的当前最大值;或者,在最后查找数组中的最高值。

伪代码

arr = [0]
for ( char in string )
   arr[char]++
mostFrequent = highest(arr)

答案 1 :(得分:2)

如果希望间隔获得有效结果,可以在序列的每个索引处构建积分分布向量。然后通过减去j + 1和i处的积分分布,你可以从s [i],s [i + 1],...,s [j]的间隔获得分布。

Python中的一些伪代码如下。我假设你的角色是字符,因此有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

答案 2 :(得分:2)

对数组执行单次迭代,并针对每个位置记住每个字符在该位置有多少次出现。所以像这样:

"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

计算此数组后,您可以在恒定时间内获得区间(i,j)中给定字母的出现次数 - 只需计算count[j] - count[i-1](此处谨慎i = 0!)。

因此,对于每个查询,您必须迭代所有字母而不是间隔中的所有字符,因此不会迭代超过10 ^ 6个字符,您最多只能传递128个字符(假设您只有ASCII符号)。 / p>

缺点 - 您需要更多内存,具体取决于您使用的字母大小。

答案 3 :(得分:1)

您需要根据空间和时间复杂度指定算法要求。

如果您坚持O(1)空间复杂性,只需排序(例如,如果没有可用的自然比较运算符,则使用位的字典排序)并计算最高元素的出现次数将为您提供{{1}时间复杂度。

如果您坚持O(N log N)时间复杂度,请使用@Luchian Grigore的解决方案,该解决方案也需要O(N)空间复杂度(嗯,O(N) O(K) - 字母字母表)

答案 4 :(得分:0)

string="something"
arrCount[string.length()];

每次访问字符串后调用freq()

freq(char accessedChar){
arrCount[string.indexOf(x)]+=1
}

获取最频繁的char调用string.charAt(arrCount.max())

答案 5 :(得分:0)

假设字符串是常量,并且将不同的ij传递给查询出现。

如果您想最小化处理时间,可以制作

struct occurences{
    char c;
    std::list<int> positions;
};

并为每个角色保留std::list<occurences>。为了快速搜索,您可以保持positions订购。

如果你想最小化内存,你可以保持一个递增的整数并循环通过i .. j

答案 6 :(得分:0)

如所建议的,最节省时间的算法是将每个字符的频率存储在数组中。但请注意,如果您只是使用字符索引数组,则可能会调用未定义的行为。也就是说,如果您正在处理包含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循环之后返回该数组而不是max字符,并省略找到最常用字符的算法部分。在顶级代码中构造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
}

现在,这仅适用于整个字符串。如果要重复处理相对较小的子字符串,则应使用第一个版本。否则,我说你最好的选择可能是做第二个解决方案,但是将字符串分成可管理的块;这样,您可以从缓存中获取大部分信息,只需重新计算迭代器所在的块中的频率。

答案 7 :(得分:0)

最快是使用unordered_map或类似名称:

distance_matrix = squareform(cosine_distances_array)
points_to_keep = []

for (i in range(len(points)-1)):
    for (j in range(i+1, len(points))):
        if(distance_matrix[i,j] > 0.4):
            points_to_keep.push((points[i], points[j]))

print points_to_keep

从内存角度来说,最轻将需要可以进行排序的非恒定输入,因此可以使用find_first_not_of或类似的输入:

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; });
}

应注意,这两个函数都具有非空字符串的先决条件。同样,如果字符串中最多的字符并列,则两个函数都将返回按字典顺序排在首位的字符最多的字符。

Live Example