所有子串的频率

时间:2015-06-05 20:00:22

标签: c++ string algorithm

我想在C ++中找到字符串中所有子串的频率。目前我正在使用以下内容:

    unordered_map<string,int> mp;
    string s;// the string of which we want all substrings... n is length
    cin>>s;
    string t;
    for(int i=0,i<=n-1;++i) // starting point of a substring
    {
        t="";
        for(int j=i;j<=n-1;++j) // all substrings startings at i
        {
            t+=s[j];
            ++mp[t];
        }
    }

我想提高它的时间复杂度。我们可以做得更好吗?是否存在更好的算法?对不起,如果这里不是主题...如果是这样的话,我会关闭它。

修改:

这就是我想出的......保持字符串的所有后缀的trie。然后遍历从i开始的所有子串,以便搜索为O(1)。

每个节点指定一个子字符串(后缀的前缀)。现在保持每个节点的频率并相应地更新它。虽然这个方法是O(n ^ 2)但是由于内存分配和将节点的每个下一个指针(26次)重置为NULL,常量相当大。我可以进一步优化它吗?还可以有更快的替代方式来存储trie而不是链表吗?我能够挤出我的解决方案,但它非常接近时限。

2 个答案:

答案 0 :(得分:0)

以这种方式思考。想象一下,你的字符串长10个字符,所有字符都不同:

`0123456789`

在这种情况下,所有子串都是唯一的。所以有O(n ^ 2)个唯一的子串。每个子字符串在字典中都需要自己的条目。确切地说,在这种情况下,(n ^ 2)/ 2 = 50个条目。

因此,在字典中插入这些子字符串至少需要50次插入操作。

当然,在一般情况下,为避免O(n ^ 2)的上限,可以做的并不多。

专注于使代码本身更快 - 我不确定您是否会找到更好的算法。

答案 1 :(得分:0)

这是原始C代码中的一个版本。它使用两个与输入字符串(s_len)长度相同的数组来计算匹配数和重复项的位置。优点是字符串永远不会在地图中重复,并且它节省了创建地图条目所需的时间(您发现它的速度要慢得多)。另一个优点是它不需要n ^ 2个存储器,它像map / reduce函数一样立即打印出信息,以便在后面的步骤中处理。它使用本地C内存函数,如calloc()bzero()memcmp()来进行有效的内存分配,归零和比较。

算法的工作原理如下:

  • 对于每个长度为len的字符串,len从1变为s_len - 1:
  • 清除数组matchesdups;
  • 沿着字符串(使用i)从开头(位置0)到结尾:
  • 如果此位置已被计为重复,请跳过它;
  • 对于字符串下方的每个位置(j = i+1stop),请将其与当前位置进行比较;
  • 如果匹配,请增加位置i的匹配数,并将位置j标记为重复;
  • 在此步行结束时,打印出长度len的匹配数。

以下是代码:

#include <stdio.h>
#include <stdlib.h>    /* for calloc() */
#include <strings.h>   /* for bzero() */

/* Find the number of matching substrings in the string s */
void sub(char *s)
{
    size_t s_len = strlen(s);
    short *matches = (short *) calloc(s_len, sizeof(short));
    short *dups = (short *) calloc(s_len, sizeof(short));
    size_t n = s_len * sizeof(short);    /* used by bzero() */
    size_t len, i, j, stop;

    /* Find all substrings of length 1..s_len */
    for (len=1; len<s_len; ++len)
    {
        bzero((void *) matches, n);    /* zero out the number of matches */
        bzero((void *) dups, n);       /* zero out the duplicates */
        stop = s_len - len + 1;
        for (i=0; i<stop; ++i)
        {   
            if (dups[i])    /* this is a duplicate (was already counted) */
                continue;   
            for (j=i+1; j<stop; ++j)
            {       
                if (memcmp(s+i, s+j, len))    /* substring comparison */
                    continue;    /* not a match? continue */
                matches[i]++;
                dups[j] = 1;
            }       
            if (matches[i])
                printf("%d: %.*s\n", matches[i]+1, (int) len, s+i);
        }   
    }
}

int main()
{
    sub("abcabcabcabc");
    return 0;
}

这是输出:

4: a
4: b
4: c
4: ab
4: bc
3: ca
4: abc
3: bca
3: cab
3: abca
3: bcab
3: cabc
3: abcab
3: bcabc
2: cabca
3: abcabc
2: bcabca
2: cabcab
2: abcabca
2: bcabcab
2: cabcabc
2: abcabcab
2: bcabcabc
2: abcabcabc