|
5 | 5 | #include <string>
|
6 | 6 | #include <algorithm>
|
7 | 7 | #include <limits>
|
| 8 | +#include <array> |
8 | 9 |
|
9 |
| -using INT = std::uint64_t; |
| 10 | +std::string str; |
| 11 | +std::vector<std::array<std::int32_t, 26>> strIndexTable; |
10 | 12 |
|
11 |
| -struct AlphabetSet { |
12 |
| - std::int32_t val; |
13 |
| - static const AlphabetSet empty; |
14 |
| - static const AlphabetSet all; |
15 |
| - constexpr auto elem(char c) const -> bool |
16 |
| - { |
17 |
| - return !!(val & (1 << (c - 'a'))); |
18 |
| - } |
19 |
| - constexpr auto insert(char c) const -> AlphabetSet |
20 |
| - { |
21 |
| - return {val | (1 << (c - 'a'))}; |
22 |
| - } |
23 |
| - constexpr auto operator==(AlphabetSet rhs) const -> bool |
24 |
| - { |
25 |
| - return val == rhs.val; |
| 13 | +// strIndexTable[i][x - 'a'] : 文字列の i 番目以降に最初に現れる文字 x のインデックス(現れない場合は -1) |
| 14 | +// となるような2次元配列 strIndexTable を初期化する。 |
| 15 | +void initStringIndexTable() |
| 16 | +{ |
| 17 | + strIndexTable.resize(str.size() + 1); |
| 18 | + for(std::size_t j = 0; j < 26; ++j) { |
| 19 | + strIndexTable.back()[j] = -1; |
26 | 20 | }
|
27 |
| - constexpr auto operator!=(AlphabetSet rhs) const -> bool |
28 |
| - { |
29 |
| - return val != rhs.val; |
| 21 | + for(std::size_t i = str.size() - 1; ; --i) { |
| 22 | + strIndexTable.at(i) = strIndexTable.at(i + 1); |
| 23 | + char x = str.at(i); |
| 24 | + strIndexTable.at(i).at(x - 'a') = static_cast<std::int32_t>(i); |
| 25 | + if (i == 0) { |
| 26 | + break; |
| 27 | + } |
30 | 28 | }
|
31 |
| -}; |
32 |
| -const AlphabetSet AlphabetSet::empty = {0}; |
33 |
| -const AlphabetSet AlphabetSet::all = {(1 << 26) - 1}; |
| 29 | +} |
34 | 30 |
|
35 |
| -auto allOccurrencesNotIn(const char *s, AlphabetSet e) -> std::vector<std::pair<char, const char *>> |
| 31 | +using Pair = std::pair<char, int>; |
| 32 | + |
| 33 | +// 文字列の i 番目以降に出現する文字と、その最初の出現位置の組 Pair(c, j) のリストを返す。 |
| 34 | +// リストの順番は、アルファベットの若い順である。 |
| 35 | +auto allOccurrences(int i) -> std::vector<Pair> |
36 | 36 | {
|
37 |
| - std::vector<std::pair<char, const char *>> res; |
38 |
| - for (; *s && e != AlphabetSet::all; ++s) { |
39 |
| - if (!e.elem(*s)) { |
40 |
| - res.emplace_back(*s, s+1); |
41 |
| - e = e.insert(*s); |
| 37 | + std::vector<Pair> res; |
| 38 | + auto const& tbl = strIndexTable.at(i); |
| 39 | + if (i < str.size()) { |
| 40 | + for (int k = 0; k < 26; ++k) { |
| 41 | + if (tbl.at(k) != -1) { |
| 42 | + res.emplace_back('a' + k, tbl.at(k)); |
| 43 | + } |
42 | 44 | }
|
43 | 45 | }
|
44 | 46 | return res;
|
45 | 47 | }
|
46 | 48 |
|
47 |
| -const char *strBase; |
48 |
| -std::vector<INT> memo; |
| 49 | +std::vector<std::int64_t> numberOfSubstringsVec; |
49 | 50 |
|
50 |
| -using Pair = std::pair<char, const char *>; |
| 51 | +// numberOfSubstringsVec[i] = 〈文字列の i 番目以降からなるスライス〉の部分文字列の個数 |
| 52 | +// となるような配列 numberOfSubstringsVec を初期化する。 |
| 53 | +// ただし、値が十分大きい場合の計算は適宜打ち切られる(十分大きい値が適当に入っている)。 |
| 54 | +void initNumberOfSubstringsVec() |
| 55 | +{ |
| 56 | + numberOfSubstringsVec.resize(str.size() + 1); |
| 57 | + const std::int64_t maxI = 1e18 + 1; // 十分大きな値 |
| 58 | + for (std::size_t i = str.size(); ; --i) { |
| 59 | + std::int64_t acc = 1; |
| 60 | + for (auto const& p : allOccurrences(i)) { |
| 61 | + if (acc > maxI) { |
| 62 | + // 値が十分大きくなった場合は計算を打ち切ってオーバーフローを回避する |
| 63 | + break; |
| 64 | + } |
| 65 | + auto y = numberOfSubstringsVec.at(p.second + 1); |
| 66 | + if (y > maxI) { |
| 67 | + // 値が十分大きくなった場合はry |
| 68 | + acc = y; |
| 69 | + break; |
| 70 | + } |
| 71 | + acc += y; |
| 72 | + } |
| 73 | + numberOfSubstringsVec.at(i) = acc; |
| 74 | + if (i == 0) { |
| 75 | + break; |
| 76 | + } |
| 77 | + } |
| 78 | +} |
51 | 79 |
|
52 |
| -auto lexIndexX(INT i, const char *s) -> std::string; |
| 80 | +auto lexIndexSlice(int j, std::int64_t i) -> std::string; |
53 | 81 |
|
54 |
| -auto lexIndexW(INT i, std::vector<Pair>::const_iterator it, std::vector<Pair>::const_iterator end, INT maxI) -> std::string |
| 82 | +// 与えられた集合 S の要素の部分文字列のうち、辞書順で i 番目となるものを返す。 |
| 83 | +// S は、文脈で与えられる文字列に対して (最初の一文字, その出現位置) という形の要素からなるリストで、アルファベットの若い順に並んでいる。 |
| 84 | +// i が大きいときは Nothing を返す。 |
| 85 | +auto lexSearch(std::int64_t i, std::vector<Pair>::const_iterator it, std::vector<Pair>::const_iterator end) -> std::string |
55 | 86 | {
|
56 | 87 | for (;;) {
|
57 | 88 | if (i == 0) {
|
58 | 89 | return "";
|
59 | 90 | } else if (it == end) {
|
60 | 91 | return "Eel";
|
61 | 92 | } else {
|
62 |
| - auto n = memo.at(it->second - strBase); |
| 93 | + auto n = numberOfSubstringsVec.at(it->second + 1); |
63 | 94 | if (i <= n) {
|
64 |
| - return it->first + lexIndexX(i - 1, it->second); |
| 95 | + return it->first + lexIndexSlice(it->second + 1, i - 1); |
65 | 96 | } else {
|
66 |
| - // return lexIndexW(i - n, ++it, end, maxI); |
| 97 | + // return lexSearch(i - n, ++it, end); |
67 | 98 | i -= n;
|
68 | 99 | ++it;
|
69 | 100 | }
|
70 | 101 | }
|
71 | 102 | }
|
72 | 103 | }
|
73 | 104 |
|
74 |
| -auto lexIndexX(INT i, const char *s) -> std::string |
| 105 | +// 〈文字列の j 番目以降からなるスライス〉の部分文字列のうち、辞書順で i 番目となるものを返す。 |
| 106 | +// i が大きいときは Nothing を返す。 |
| 107 | +auto lexIndexSlice(int j, std::int64_t i) -> std::string |
75 | 108 | {
|
76 |
| - auto t = allOccurrencesNotIn(s, AlphabetSet::empty); |
77 |
| - std::sort(t.begin(), t.end(), [](Pair const& a, Pair const& b) { return a.first < b.first; }); |
78 |
| - return lexIndexW(i, t.begin(), t.end(), i); |
| 109 | + auto t = allOccurrences(j); |
| 110 | + return lexSearch(i, t.begin(), t.end()); |
79 | 111 | }
|
80 | 112 |
|
81 | 113 | int main()
|
82 | 114 | {
|
83 |
| - std::string str; |
84 |
| - // std::ifstream ins("testinput.txt"); |
85 |
| - auto& ins = std::cin; |
86 |
| - std::getline(ins, str); |
87 |
| - INT k = 0; |
88 |
| - ins >> k; |
89 |
| - strBase = str.c_str(); |
90 |
| - { |
91 |
| - memo.resize(str.size() + 1); |
92 |
| - for (const char *s = strBase + str.size(); /* s >= strBase */; --s) { |
93 |
| - INT val = 1; |
94 |
| - { |
95 |
| - AlphabetSet e = AlphabetSet::empty; |
96 |
| - for (const char *t = s; *t && e != AlphabetSet::all; ++t) { |
97 |
| - if (!e.elem(*t)) { |
98 |
| - val += memo.at(t+1 - strBase); |
99 |
| - if (val > k) { |
100 |
| - break; |
101 |
| - } |
102 |
| - e = e.insert(*t); |
103 |
| - } |
104 |
| - } |
105 |
| - } |
106 |
| - /* |
107 |
| - auto const& t = allOccurrencesNotIn(s, AlphabetSet::empty); |
108 |
| - for (auto const& u : t) { |
109 |
| - val += memo.at(u.second - strBase); |
110 |
| - if (val > k) { |
111 |
| - break; |
112 |
| - } |
113 |
| - } |
114 |
| - */ |
115 |
| - memo.at(s - strBase) = val; |
116 |
| - if (s == strBase) { |
117 |
| - break; |
118 |
| - } |
119 |
| - } |
120 |
| - } |
121 |
| - std::cout << lexIndexX(k, str.c_str()) << std::endl; |
| 115 | + std::getline(std::cin, str); |
| 116 | + std::int64_t k = 0; |
| 117 | + std::cin >> k; |
| 118 | + initStringIndexTable(); |
| 119 | + initNumberOfSubstringsVec(); |
| 120 | + std::cout << lexIndexSlice(0, k) << std::endl; |
122 | 121 | }
|
0 commit comments