【问题标题】:String similarity in cc中的字符串相似度
【发布时间】:2012-01-15 07:30:23
【问题描述】:

对于两个字符串 A 和 B,我们将字符串的相似度定义为两个字符串共有的最长前缀的长度。例如,字符串“abc”和“abd”的相似度为2,而字符串“aaa”和“aaab”的相似度为3。 计算字符串 S 与其每个后缀的相似度总和

这是我的解决方案...

#include<stdio.h>
#include<string.h>
int getSim(char str[],int subindex)
{
    int l2=subindex
    int i=0;
    int count=0;
    for(i=0;i<l2;i++)
        if(str[i]==str[subindex])
        {
            count++;
            subindex++;
        }
        else
            break;
    return count;   
}
int main()
{
    int testcase=0;
    int len=0;
    int sum=0;
    int i=0;
    char s[100000];
    scanf("%d",&testcase);
    while(testcase--)
    {
        sum=0;
        scanf("%s",s);
        for(i=0;i<strlen(s);i++)
            if(s[i]==s[0])
            {
                sum=sum+getSim(s,i);
            }
        printf("%d\n",sum);
    }
}

我们如何使用后缀数组来解决这个问题??

【问题讨论】:

  • 你觉得你的程序输出有什么不正确的地方?如果您正在寻找代码审查,请尝试 codereview.stackexchange... StackOverflow 适用于您需要回答的特定问题。
  • 为什么说后缀数组?那不应该是一个 prefix 吗?
  • @glowcoder 我的程序的输出没有任何问题......但是当输入大小增加时它有点慢......比如说 100000 长度的字符串......所以有人建议使用后缀数组.....

标签: c suffix-array


【解决方案1】:

我不确定这是否是最好的算法,但这里是解决方案。

首先,构建后缀数组。朴素算法(将所有后缀放入数组然后排序)非常慢 - O(n^2 * log(n)) 操作,有几种算法可以在 O(nlogn) 时间内完成。

我假设字符串是 0 索引的。

  1. 现在,取字符串s 中的第一个字母l,并使用二分法查找后缀数组中以l 开头的第一个字符串的索引i,然后另一个二进制搜索来查找范围 [i..n] 中第一个字符串的索引 j,它不以 l 开头。然后,您将拥有 [i..j-1] 范围内的所有字符串都以相同的字母 l 开头。所以问题的答案至少是 j-i。

  2. 然后对范围 [i..j) 中的字符串应用类似的过程。取第二个字母l2,找到第一个字符串s[i2]对应的索引i2j2使得s[i2][1] == l2和第一个字符串s[j2]对应s[j2][1] != l2。将 j2-i2 添加到答案中。

  3. 重复此过程 n 次,直到您用完原始字符串中的字母。问题的答案是j1-i1 + j2-i2 + ... + jn-in

【讨论】:

    【解决方案2】:

    你在cmets中提到它是正确的,但是它很慢。

    在Java中,你可以通过s.length()获取String的长度——这个值被缓存在对象中,通过O(1)来获取。

    但是当你转到 C 时,你会得到一个带有 strlen(s) 的字符串的长度,它每次都会重新计算(在 O(n) 中)。所以当你应该做O(n)时,因为你有一个O(n)操作在那里,整个函数变成O(n^2)

    要解决此问题,请在运行时将值缓存一次。这会让你回到线性时间。

    不好:

    scanf("%s",s);
    for(i=0;i<strlen(s);i++)
        if(s[i]==s[0])
        {
            sum=sum+getSim(s,i);
        }
    

    好:

    scanf("%s",s);
    strlen = strlen(s); /* assume you declared "int strlen" earlier */
    for(i=0;i<strlen;i++) /* this is now constant time to run */
        if(s[i]==s[0])
        {
            sum=sum+getSim(s,i);
        }
    

    【讨论】:

    • 这不会为具有高相似性的输入提供线性时间,getSim() 也可以是 O(n)。考虑memset(str, 'a', n)的情况。
    • @Daniel 很公平。我实际上并不知道他的算法的当前时间。我假设它是O(n),因为这看起来就像(称之为程序员直觉)在O(n) 时间完成的事情。无论他当前的时间是多少,他都可以通过上述技巧将时间减少n
    • 仅当循环内完成的工作为 O(1) 时。当然,只计算一次strlen() 总是一件好事。这让你从 O(n*(n + body)) 到 O(n*body),但是如果 body 也是 O(n),那么它是一个常数因子减少,如果 body 是 O(n^2) ),这是花生,如果body 为 O(sqrt(n)),则它减少了 sqrt(n) 的一个因子。
    • @Daniel ummm 不完全是花生。如果正文是O(n^2),那么如果没有缓存strlen,你的总数是O(n^3)。从立方到二次是速度的巨大提升。事实上,body 的效率越低,缓存的增加就越好。无论如何,它始终是n 的一个因素。
    • 没有。说bodysum += strlen(s);。如果没有缓存,每次迭代你有两个 strlen() 调用,缓存你有一个。您不是将body 的成本与strlen() 的成本相乘,而是将它们相加。
    猜你喜欢
    • 2011-10-20
    • 2016-07-15
    • 1970-01-01
    • 2011-12-10
    • 2013-02-24
    • 2011-04-04
    • 2015-10-19
    • 2019-05-01
    • 1970-01-01
    相关资源
    最近更新 更多