我想在允许重复的子字符串时找到给定字符串的字典顺序的第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;
}
的链接
答案 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个子字符串一次,所以解决方法方法基本上使用相同的逻辑但具有不同的实现。