P3975 [TJOI2015]弦论
题目大意
对于一个给定的长度为 \(n(1\le n\le5\cdot10^5)\) 的字符串,求出它的第 \(k(1\le k\le10^9)\) 小子串,输入 \(t\) , \(t=0\) 时不同位置的相同子串算作一个, \(t=1\) 时算作多个,如果子串数量 \(<k\) ,输出 \(-1\)。
思路
\(sam\) 中本质不同的子串个数为从初始节点出发的总路径数,可以对每个节点求出从其出发的总路径数 \(f_v\) ,代表该到达该节点后还能获得的不同子串数,对 \(sam\) 进行拓扑排序,有 \(f_v=1+\sum_{(u,v)\in sam}f_u\) 。如果不同位置的子串算作多个,将 \(1\) 改为 \(|right_v|\) 即可,注意不要算入空串。之后从初始节点开始贪心即可求得答案。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<int, int>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mk make_pair
//#define int LL
#define lc P*2
#define rc P*2+1
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000009;
const LL mod = 998244353;
const int maxn = 500010;
string S;
LL T, K;
int tot = 1, last = 1;
struct Node {
int len, fa;
int ch[26];
bool isnp;
}sam[maxn * 3];
vector<int>G[maxn * 3], SAM[maxn * 3];
LL siz[maxn * 3], f[maxn * 3], g[maxn * 3], in[maxn * 3];
queue<int>que;
void add_edge(int from, int to)
{
G[from].push_back(to);
}
void add_edge2(int from, int to)
{
SAM[from].push_back(to);
}
void extend(char c)
{
int p = last, np = last = ++tot;
sam[np].len = sam[p].len + 1;
sam[np].isnp = true;
for (; p && !sam[p].ch[c]; p = sam[p].fa)
sam[p].ch[c] = np;
if (!p)
sam[np].fa = 1;
else
{
int q = sam[p].ch[c];
if (sam[q].len == sam[p].len + 1)
sam[np].fa = q;
else
{
int nq = ++tot;
sam[nq] = sam[q], sam[nq].len = sam[p].len + 1;
sam[nq].isnp = false;
sam[q].fa = sam[np].fa = nq;
for (; p && sam[p].ch[c] == q; p = sam[p].fa)
sam[p].ch[c] = nq;
}
}
}
void dfs1(int v)
{
siz[v] = sam[v].isnp;
for (auto& to : G[v])
{
dfs1(to);
siz[v] += siz[to];
}
}
void solve()
{
for (int i = 0; i < S.size(); i++)
extend(S[i] - 'a');
for (int i = 2; i <= tot; i++)
add_edge(sam[i].fa, i);
for (int i = 1; i <= tot; i++)
{
for (int j = 0; j < 26; j++)
{
if (sam[i].ch[j])
add_edge2(sam[i].ch[j], i), in[i]++;
}
}
for (int i = 1; i <= tot; i++)
{
if (in[i] == 0)
que.push(i);
}
dfs1(1);
while (!que.empty())
{
int v = que.front();
que.pop();
if (v != 1)
f[v]++, g[v] += siz[v];
for (auto& to : SAM[v])
{
f[to] += f[v], g[to] += g[v];
in[to]--;
if (in[to] == 0)
que.push(to);
}
}
int now = 1;
string ans = "";
if (T == 0)
{
if (f[1] < K)
{
cout << -1 << endl;
return;
}
while (true)
{
if (now > 1)
{
if (K == 1)
break;
K--;
}
for (int i = 0; i < 26; i++)
{
if (!sam[now].ch[i])
continue;
if (f[sam[now].ch[i]] < K)
K -= f[sam[now].ch[i]];
else
{
ans += i + 'a';
now = sam[now].ch[i];
break;
}
}
}
}
else
{
if (g[1] < K)
{
cout << -1 << endl;
return;
}
while (true)
{
if (now > 1)
{
if (K <= siz[now])
break;
K -= siz[now];
}
for (int i = 0; i < 26; i++)
{
if (!sam[now].ch[i])
continue;
if (g[sam[now].ch[i]] < K)
K -= g[sam[now].ch[i]];
else
{
ans += i + 'a';
now = sam[now].ch[i];
break;
}
}
}
}
cout << ans << endl;
}
int main()
{
IOS;
cin >> S >> T >> K;
solve();
return 0;
}