【问题标题】:Finding the K th lexicographically substring of a given string when the duplicate substrings are allowed当允许重复子字符串时,查找给定字符串的第 K 个字典顺序子字符串
【发布时间】:2015-09-08 10:44:51
【问题描述】:

当允许重复子字符串时,我想找到给定字符串的字典序上第 K 个最小的子字符串

假设给定一个字符串abc,那么它的子字符串按字典顺序是{a,ab,abc,b,c},现在假设给定K = 3 那么ans就是abc

现在假设给定字符串 aaa 那么它的所有子字符串都是 {a,a,a,aa,aaa,aa} 所以现在如果 K = 4 那么我们输出 aa

但是,我在 codeforces 上遇到了以下代码,但我无法理解。非常感谢任何帮助。

char s [MaxN];
bool b [MaxN];
int k, n;

void solve (vector <int> v)
{
   int i, j;
   int64 p, q;
   char c;

   vector <int> w;
   for (c = 'a'; c <= 'z'; c++)
   {
       p = q = 0;
       w.clear ();
       for (j = 0; j < (int) v.size (); j++)
       {
           i = v[j];
           if (s[i] == c)
           {
               w.push_back (i + 1);
               p++;
               q += n - i;
           }
      }
      if (k < q)
          break;
      k -= q;
   }
   assert (c <= 'z');

   putchar (c);
   if (k < p)
       return;
   k -= p;
   solve (w);
}

int main (void)
{
    int i;

    while (scanf (" %s %d", s, &k) != EOF)
    {
         n = (int) strlen (s);
         if (k > ((((int64) n) * (int64) (n + 1)) >> 1LL))
         {
             printf ("No such line.\n");
             continue;
         }
         k--;

         vector <int> v;
         for (i = 0; i < n; i++)
         v.push_back (i);
         solve (v);
         putchar ('\n');
     }
     return 0;
}

这里是问题http://codeforces.com/problemset/problem/128/B的链接

【问题讨论】:

  • 代码中是否有部分你不知道它的意思?您是否尝试过通过一些简单的案例逐步了解它的作用?
  • 我尝试在一些小测试用例上运行它,但在大多数情况下,我迷失在两者之间,也无法弄清楚背后的想法。任何新想法都同样受欢迎

标签: c++ string suffix-array


【解决方案1】:

Shubajit Saha 使用 Suffix Array 的方法不正确。考虑字符串abaab,其排序后的后缀为(从0开始):

2. aab
3. ab
0. abaab
4. b
1. baab

按照他的说法,从后缀2. aab得到的子串aab小于从3. ab得到的子串a,这显然是错误的!

正确的方法是使用后缀自动机 + 动态编程。您可以在CP-Algorithm 了解它。这是一篇写得很好的关于 Suffix Automaton 的教程。

按字典顺序排列的第 k 个子串对应于后缀自动机中按字典顺序排列的第 k 个路径。因此,在计算每个状态的路径数之后,我们可以很容易地从自动机的根开始搜索第 k 条路径。请注意,此问题允许重复的子字符串,因此您应该使用以该状态结尾的子字符串的数量来初始化,而不是用 1 来初始化每个状态。为此,请参阅教程中的Number of occurrences 部分。

这种方法的一个优点是时间复杂度不取决于k,它只取决于子字符串的长度(最多为n)。因此,k 可以远大于问题中的约束(1e10 很好)。

这是我的 C++ 代码:https://codeforces.com/contest/128/submission/72601519

时间复杂度为O(nlgn)。虽然构建后缀自动机并遍历它是O(n),但我们需要按状态长度对状态进行排序。我使用 STD 排序,即O(nlgn)。您可以改用计数排序使其成为线性。

【讨论】:

    【解决方案2】:

    解决这个问题的标准方法是构造一个给定字符串的后缀数组。后缀数组以字典顺序为我们提供给定字符串的所有后缀。一旦我们拥有给定字符串的所有排序后缀,就会观察到字符串的每个子字符串都是某个后缀的前缀。如果我们按字典序升序遍历每个大小为 l 的后缀的后缀,则得到 l 个子字符串,每个子字符串都小于从字典序上大于我们正在考虑的后缀的后缀获得的子字符串.因此我们可以很容易地通过对子串的计数来找到第 K 个最小的子串。

    实际上,后缀数组用于解决这个问题的一个更困难的版本,其中给出了 Q 个查找第 K 个子字符串的查询,其中 K 在每个查询中是不同的。

    关于您在问题中的代码,由于您只需找到第 k 个子字符串一次,因此解决方案方法本质上使用相同的逻辑但具有不同的实现。

    【讨论】:

      猜你喜欢
      • 2016-04-13
      • 2023-01-19
      • 2012-04-20
      • 1970-01-01
      • 2014-05-09
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      相关资源
      最近更新 更多