当允许重复子字符串时,查找给定字符串的第K个子字符串

Finding the K th lexicographically substring of a given string when the duplicate substrings are allowed

本文关键字:字符串 许重复 查找      更新时间:2023-10-16

我想在允许重复子字符串的情况下找到给定字符串的字典顺序第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

Shubajit Saha使用后缀数组的方法是不正确的。考虑字符串abaab,其排序后的后缀为(基于0):

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

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

正确的方法是使用后缀自动机+动态规划。你可以在CP-Algorithm了解它。这是一个写得很好的关于后缀自动机的教程。

按字典顺序排列的第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)。您可以使用计数排序来使其线性化。

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

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

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