【问题标题】:Longest prefix string length for all the suffixes所有后缀的最长前缀字符串长度
【发布时间】:2012-01-05 08:13:59
【问题描述】:

找出字符串所有后缀的最长前缀字符串的长度。

例如字符串ababaa的后缀是ababaababaaabaabaaaaa。这些字符串中的每一个与字符串“ababaa”的相似度分别为 6,0,3,0,1,1。因此答案是 6 + 0 + 3 + 0 + 1 + 1 = 11。

我写了以下代码

#include <iostream>
#include <string.h>

#include <stdio.h>
#include <time.h>

int main ( int argc, char **argv) {
    size_t T;
    std::cin >> T;
    char input[100000];
    for ( register size_t i = 0; i < T; ++i) {
        std::cin >> input;

        double t = clock();

        size_t len    = strlen(input);
        char *left    = input;
        char *right   = input + len - 1;
        long long sol = 0;
        int end_count = 1;
        while ( left < right ) {
            if ( *right != '\0') {
                if ( *left++ == *right++ ) {
                    sol++;
                    continue;
                }
            }
            end_count++;
            left = input; // reset the left pointer
            right = input + len - end_count; // set right to one left.
        }
        std::cout << sol + len << std::endl;
        printf("time= %.3fs\n", (clock() - t) / (double)(CLOCKS_PER_SEC));
    }
}

工作正常,但是对于一个长度为100000 且具有相同字符(即aaaaaaaaaa.......a)的字符串,它需要很长时间,我该如何进一步优化它。

【问题讨论】:

  • 这似乎是一个惊人的普遍问题。 Here 是一个 O(n) 算法(免责声明:我还没有验证算法)。

标签: c++ algorithm data-structures


【解决方案1】:

可以使用后缀数组:http://en.wikipedia.org/wiki/Suffix_array

【讨论】:

  • 怎么样?在我看来,创建后缀数组比上面的算法需要更长的时间(并且使用更多的内存)。即使在创建之后,它是否会增加工作量,因为您正在重复进行二进制搜索以检查子字符串,而上面的算法直接针对候选字符?也许我错了 - 只考虑了一两分钟 - 但一些示例代码将有助于确定....
  • 该数组可以在 O(n log n ) 时间内构建(抱歉,我不记得细节了)——你只做一次,作为任务预处理。但这是值得的,因为通常您需要在同一文本上多次找到最长的前缀,每次这样的搜索只需 O(m + log n)。
【解决方案2】:

假设您的 ababaa 是模式 P。 我认为您可以使用以下算法:

  1. 为 P 的所有可能后缀创建一个后缀自动机。
  2. 使用 P 作为输入遍历自动机,计算到目前为止遍历的边数。对于自动机的每个接受状态,将当前边缘计数添加到总和。走自动机,直到到达输入的末尾,或者没有更多的边缘可以通过。
  3. 总和就是结果。

【讨论】:

    【解决方案3】:

    使用Z算法计算所有子字符串的长度,它也以O(n)为前缀,然后扫描结果数组并将其值求和。

    参考:https://www.geeksforgeeks.org/sum-of-similarities-of-string-with-all-of-its-suffixes/

    【讨论】:

      【解决方案4】:

      据我所见,您正在使用普通数组来评估后缀,虽然它可能对某些数据集有效,但在某些情况下效率不高,例如您提到的那种。

      您需要实现 Prefix-TreeTrie 之类的数据结构。这些代码并不简单,所以如果你不熟悉它们,我建议你阅读一下它们。

      【讨论】:

        【解决方案5】:

        我不确定 Trie 是否会给您带来很大的性能提升.. 但我肯定会考虑一下。

        我的另一个想法是尝试压缩您的字符串。我并没有真正考虑过,只是一个疯狂的想法......

        如果你有这样的字符串:ababaa 压缩它可能是:abab2a。然后你必须想出一种技术,你可以在这些字符串中使用你的算法。优点是您可以有效地比较长字符串100000a。或者更重要的是:您可以非常快速地计算总和。

        但是,我还是没有考虑清楚,也许这是一个非常糟糕的主意;)

        【讨论】:

          【解决方案6】:

          这里是一个java实现:

                  // sprefix
                  String s = "abababa";
                  Vector<Integer>[] v = new Vector[s.length()];
                  int sPrefix = s.length();
                  v[0] = new Vector<Integer>();
                  v[0].add(new Integer(0));
                  for(int j = 1; j < s.length(); j++)
                  {
                      v[j] = new Vector<Integer>();
                      v[j].add(new Integer(0));
                      for(int k = 0; k < v[j - 1].size(); k++)
                          if(s.charAt(j) == s.charAt(v[j - 1].get(k)))
                          {
                              v[j].add(v[j - 1].get(k) + 1);
                              v[j - 1].set(k, 0);
                          }
                  }
          
                  for(int j = 0; j < v.length; j++)
                      for(int k = 0; k < v[j].size(); k++)
                          sPrefix += v[j].get(k);
          
                  System.out.println("Result = " + sPrefix);
          

          【讨论】:

            猜你喜欢
            • 2019-05-03
            • 2018-12-02
            • 2017-01-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-10-11
            • 1970-01-01
            • 2020-06-22
            相关资源
            最近更新 更多