Skip to content

Commit 26110d1

Browse files
committed
TDPC-G: Fast solution (C++)
1 parent a0f6593 commit 26110d1

File tree

1 file changed

+78
-79
lines changed

1 file changed

+78
-79
lines changed

tdpc-g/main.cpp

+78-79
Original file line numberDiff line numberDiff line change
@@ -5,118 +5,117 @@
55
#include <string>
66
#include <algorithm>
77
#include <limits>
8+
#include <array>
89

9-
using INT = std::uint64_t;
10+
std::string str;
11+
std::vector<std::array<std::int32_t, 26>> strIndexTable;
1012

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;
2620
}
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+
}
3028
}
31-
};
32-
const AlphabetSet AlphabetSet::empty = {0};
33-
const AlphabetSet AlphabetSet::all = {(1 << 26) - 1};
29+
}
3430

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>
3636
{
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+
}
4244
}
4345
}
4446
return res;
4547
}
4648

47-
const char *strBase;
48-
std::vector<INT> memo;
49+
std::vector<std::int64_t> numberOfSubstringsVec;
4950

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+
}
5179

52-
auto lexIndexX(INT i, const char *s) -> std::string;
80+
auto lexIndexSlice(int j, std::int64_t i) -> std::string;
5381

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
5586
{
5687
for (;;) {
5788
if (i == 0) {
5889
return "";
5990
} else if (it == end) {
6091
return "Eel";
6192
} else {
62-
auto n = memo.at(it->second - strBase);
93+
auto n = numberOfSubstringsVec.at(it->second + 1);
6394
if (i <= n) {
64-
return it->first + lexIndexX(i - 1, it->second);
95+
return it->first + lexIndexSlice(it->second + 1, i - 1);
6596
} else {
66-
// return lexIndexW(i - n, ++it, end, maxI);
97+
// return lexSearch(i - n, ++it, end);
6798
i -= n;
6899
++it;
69100
}
70101
}
71102
}
72103
}
73104

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
75108
{
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());
79111
}
80112

81113
int main()
82114
{
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;
122121
}

0 commit comments

Comments
 (0)