對於乙個給定長度為n的字串,求它的第k小子串是什麼。其中可能有重複和不重複的k小子串。
我只會字尾陣列
sa做法
當t=0的詢問,我們從sa[i]開始,每次sa[i]這個字尾,我們會加入n-sa[i]+1個子串,其中height[i]個是和sa[i-1]重複的,所以我們記乙個變數每次加上n+1-height[i]-sa[i],一旦超過k,則從sa[i]開始長度為k-pre+height[i]的串,為什麼是k-pre+height[i]呢?我個人理解是k-pre表示從sa[i]開始還需要k-pre個字元,然而前height[i]和上一次算的重複了,所以還要加上height[i].
t=1:二分答案是第幾小的串(可重複的),然後判斷有多少個串比它小(可重複的)
假設當前的串在sa上排i,長度為len,那麼排名在他之前的i−1個串的都比他小,排名在他之後的串,看兩個串的最長公共字首長度即可,首先二分出乙個位置滿足這個串被sa[i]的字尾包含,在之前比該串小的可以直接加,之後的我們發現和sa[i]的最長公共字首就是比該串小的,然後for一下加上去就好了
#include#include#include#include#includeusing namespace std;
const int maxn = 500005;
int n, m, i, j, k, sa[maxn], rank[maxn], height[maxn], w[maxn], x[maxn], tot, ty;
long long sum[maxn], sum1[maxn];
char c[maxn];
inline bool check(int p)
int begi = 0, hi = 0;
long long tot = 0;
if (sum[l] >= p) begi = l + 1, hi = p - sum[l - 1] + height[l], tot = hi + sum1[l - 1];
else begi = r + 1, hi = p - sum[r - 1] + height[r], tot = hi + sum1[r - 1];
if (tot >= k) return 1;
for(int i = begi; i <= n; i ++)
return 0;
}int main()
if (m == n) break;
for(i = 1; i <= n; i ++)
swap(rank[i], x[i]);
}tot = 0;
for(i = 1; i <= n; i ++)
cin >> ty >> k;
if (!ty)}}
else
int ans = 0;
if (check(l)) ans = l;
else if (check(r)) ans = r;
else
l = 1, r = n;
while (l != r - 1)
int begi = 0, end = 0;
if (sum[l] >= (long long)ans) begi = l, end = ans - sum[l - 1] + height[l];
else begi = r, end = ans - sum[r - 1] + height[r];
int jj = sa[begi] + end;
for(i = sa[begi]; i < jj; i ++)
printf("%c", c[i]);
}}
bzoj3998 字尾自動機
對於乙個給定長度為n的字串,求它的第k小子串是什麼。第一行是乙個僅由小寫英文本母構成的字串s 第二行為兩個整數t和k,t為0則表示不同位置的相同子串算作乙個。t 1則表示不同位置的相同子串算作多個。k的意義如題所述。輸出僅一行,為乙個數字串,為第k小的子串。如果子串數目不足k個,則輸出 1 aabc...
bzoj 3998 (字尾自動機)
給你乙個長度為 n 的字串 str 和乙個數 k 現在有兩個詢問 1.op 0 不同位置的相同子串算作乙個,求字典序第 k 小子串 2.op 1 不同位置的相同子串算作多個,求字典序第 k 小子串 因為字尾自動機能夠包含所有的子串,因此我們考慮在字尾自動機上貪心的跳轉。我們設字尾自動機上第 i 號結...
BZOJ3998 弦論 字尾自動機
題意 給定乙個長度為n的字串,求他的第k小子串是什麼。分析t 0的時候,這個題跟spoj sublex的做法一樣,當t 1的時候,不同位置的子串算多個,那麼初始化的時候d u cnt u 沒走乙個字元不是k 1而是k cnt u 1 include 2 include 3 include 4 inc...