如何在字符串中找到每个后缀的出现次数?

问题描述 投票:2回答:1

我想找到O(nlogn)或O(n)时间内原始字符串中每个字符串后缀出现的次数。

例如,对于字符串aba,后缀a出现两次,ba出现一次,aba出现一次。

c++ string algorithm hash suffix-array
1个回答
3
投票

后缀阵列解决方案

构造字符串S的后缀树以及LCP数组。这将有助于计算每个后缀的所有出现次数。

没有学习什么后缀数组和LCP,它很难理解。

suffix array

LCP

kasai’s Algorithm for Construction of LCP array from Suffix Array

让我们拿一个示例字符串并创建其后缀数组。考虑字符串S =“ABABBAABB”。

suffix positions(pos)   Suffixes of S   LCP array of S
    5                   AABB            1
    0                   ABABBAABB       2
    6                   ABB             3
    2                   ABBAABB         0
    8                   B               1
    4                   BAABB           2
    1                   BABBAABB        1
    3                   BBAABB          2
    7                   BB              not Defined

第一列(pos数组)是后缀数组中排序后缀的原始起点。我们将第二列称为SuffixArray(我们不需要计算它,它只是用于可视化)。

现在,我们知道LCP [i] = SuffixArray [i]和SuffixArray [i + 1]之间最长公共前缀的长度。例如LCP1 = LCP( “ABABBAABB”, “ABB”)= 2。

令Count [i] =从位置i开始的后缀出现次数。

for (int i = 0; i < n; )
{
    int j=i;
    while(LCP[j]==n-pos[j]){ // loop if SuffixArray[j] is a prefix of SuffixArray[j+1] 
        j++;
    }
    int incr=1;
    for (int k = j-1; k>= i ; --k)
    {
        count[ pos[k] ] = incr;
        incr++;
    } 
    i=j+1;
}

这是高度优化的解决方案,如果仔细观察所有步骤,复杂性为O(n log n)。

希望能帮助到你。如果您在第一次尝试时不理解,请再次检查所有内容。


编辑:计数数组的计算中有一个小错误。基本上我的问题是在LCP数组中找到小于当前值的下一个索引。我正在提供正确的实施。

stack< int > stack;

count[ pos[n-1] ] = 1;

for(int i=n-2;i>=0;i--){
    while(!stack.empty() and LCP[stack.top()]>=LCS[i]){
        stack.pop();
    }

    if( LCP[i] == n-pos[i]  ){
        if (stack.empty())
        {
            count[ pos[i] ] = n-i ;
        }else{
            count[ pos[i] ] = stack.top()-i ;
        }

    }else{
        count[ pos[i] ] = 1;
    }

    stack.push(i);

}

next smaller element in array


© www.soinside.com 2019 - 2024. All rights reserved.