【问题标题】:Find most common pair of characters in a string查找字符串中最常见的一对字符
【发布时间】:2012-12-08 10:06:29
【问题描述】:

我写了以下函数

//O(n^2)
void MostCommonPair(char * cArr , char * ch1 , char * ch2 , int * amount)
{
    int count , max = 0;
    char cCurrent , cCurrent2;
    int i = 0 , j;
    while(*(cArr + i + 1) != '\0')
    {
        cCurrent = *(cArr + i);
        cCurrent2 = *(cArr + i + 1);
        for(j = i , count = 0 ; *(cArr + j + 1) != '\0' ; j++)
        {
            if(cCurrent ==  *(cArr + j) && cCurrent2 ==  *(cArr + j + 1))
            {
                count++;
            }
        }
        if(count > max)
        {
            *ch1 = cCurrent;
            *ch2 = cCurrent2;
            max = *amount = count;
        }
        i++;
    }
}

对于下面的输入

“xdshahaalohalobscxbsbsbs”

ch1 = b ch2 = s 数量 = 4

但在我看来这个函数效率很低,有没有办法只遍历字符串一次或将运行大小减少到 O(n)?

【问题讨论】:

  • 请注意,OP 正在寻找具有最高计数的连续 PAIR 字符。

标签: c string algorithm function


【解决方案1】:

由于char 最多可以容纳 256 个值,您可以设置一个 [256*256] 计数器的二维表,遍历您的字符串一次,递增对应于字符串中每对字符的计数器.然后,您可以浏览 256x256 数字表,选择最大的计数,并通过查看其在 2D 数组中的位置来了解它属于哪对。由于计数器表的大小固定为与字符串长度无关的常数值,因此该操作是O(1),即使它需要两个嵌套循环。

int count[256][256];
memset(count, 0, sizeof(count));
const char *str = "xdshahaalohalobscxbsbsbs";
for (const char *p = str ; *(p+1) ; p++) {
    count[(int)*p][(int)*(p+1)]++;
}
int bestA = 0, bestB = 0;
for (int i = 0 ; i != 256 ; i++) {
    for (int j = 0 ; j != 256 ; j++) {
        if (count[i][j] > count[bestA][bestB]) {
            bestA = i;
            bestB = j;
        }
    }
}
printf("'%c%c' : %d times\n", bestA, bestB, count[bestA][bestB]);

这是link to a demo on ideone

请记住,虽然这是渐近最快的解决方案(即它是O(N),并且你不能让它比O(N) 更快),但对于较短的字符串来说性能并不好。事实上,您的解决方案可以轻松应对少于大约 256 个字符的输入,甚至可能更多。您可以对这段代码应用许多优化,但我决定不添加它们,以使代码的主要思想以最纯粹和最简单的形式清晰可见。

【讨论】:

  • 但计数最高的两个字符可能不会在字符串中的任何位置配对。他正在寻找计数最高的一对。
  • O(n),但有些输入的性能会很差。对于这个算法,它是短字符串。一个 5 个字符的字符串,它将遍历 5 个字符,然后遍历 65K 个计数。
  • @hatchet 这是非常正确的。有时,渐近性能非常好,但是设置或收集数据所需的持续额外时间是一个交易破坏者。特别是,对于短于 256 个字符的字符串,此算法的性能将比本机算法差。
  • 您可以通过保留另一个 256 个数组来改进这一点,该数组仅保留单个字符的计数。如果 char 的计数为 0 或小于当前最佳值,则在查看计数时无需查看它是第一个字符的任何对。
【解决方案2】:

如果您想要 O(n) 运行时,您可以使用 hashtable(例如,Java 的 HashMap

  • 只遍历字符串一次,一次 1 个字符 O(n)
  • 对于每个访问的字符,向前看正好 1 个字符(这就是你的字符对 - 只需将它们连接起来)O(1)
  • 对于找到的每个这样的字符对,首先在哈希表中查找它:O(1)
    • 如果它还没有在哈希表中,将其添加到字符对作为键,int 1 作为值(这会计算你在字符串中看到它的次数)。 O(1)
    • 如果它已经在哈希表中,增加它的值 O(1)
  • 查看完字符串后,检查哈希表中计数最高的对。 O(m)(其中m 是可能配对的数量;m <= n 必须)

【讨论】:

    【解决方案3】:

    是的,您可以通过保持运行计数在近似线性的时间内完成此操作。

    这有帮助吗?

    【讨论】:

      【解决方案4】:

      假设大多数“共同对”是指最常见的两个连续字符集


      在你想要的伪代码级别

       Read the first character into the "second character" register
       while(there is data)
          store the old second character as the new first character
          read the next character as the second one
          increment the count associated with this pair
       Select the most common pair
      

      因此,您需要一种有效的算法来存储和计数与字符对相关联的字符,并找到最常见的字符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-29
        • 2020-02-23
        • 1970-01-01
        • 2019-07-16
        • 2014-12-14
        • 2011-02-03
        • 2014-05-24
        • 2017-03-30
        相关资源
        最近更新 更多