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

时间:2015-09-08 10:44:51

标签: c++ string suffix-array

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

的链接

2 个答案:

答案 0 :(得分:2)

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

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

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

正确的方法是使用后缀自动机+动态编程。您可以在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),但我们需要按状态的长度对状态进行排序。我使用的是O(nlgn)的STD排序。您可以改为使用计数排序使其线性。

答案 1 :(得分:1)

此问题的标准方法是构造给定字符串的后缀数组。后缀数组以lexicographic-ally排序顺序为我们提供给定字符串的所有后缀。一旦我们得到给定字符串的所有排序后缀,就会发现字符串的每个子字符串都是某个后缀的前缀。如果我们以字典 - 升序顺序遍历每个后缀大小为l的后缀,则给出l个子字符串,每个子字符串小于从后缀中获得的子字符串,这些字符串的字典大于我们正在考虑的后缀。因此,我们可以通过腌制子字符串数来轻松找到第K个最小子字符串。

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

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