Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2ddf97e
Modify suffix array algorithm to use K-based sorting
lrvideckis Jan 25, 2026
0119dd6
[auto-verifier] verify commit 2ddf97ee0467ba68a244476f29077fd45c6520af
web-flow Jan 25, 2026
711cb9b
Update suffix array test to include LCP verification
lrvideckis Jan 26, 2026
22c7c08
[auto-verifier] verify commit 711cb9b1b87b2589cb24ee0a01714b46711ec12f
web-flow Jan 26, 2026
4ee9358
Fix loop range in suffix array test
lrvideckis Jan 26, 2026
2cb9676
Fix condition in suffix array test case
lrvideckis Jan 26, 2026
ed8ee91
[auto-verifier] verify commit 2cb9676df77940c59eacea9665150f6e707c96b7
web-flow Jan 26, 2026
9f834a9
Update suffix_array_short.hpp
lrvideckis Jan 26, 2026
76cc0f9
Fix suffix array index handling in LCP calculation
lrvideckis Jan 26, 2026
3ffb893
Fix LCP calculation in suffix array implementation
lrvideckis Jan 26, 2026
68349dd
Fix lcp calculation in suffix_array_short.hpp
lrvideckis Jan 26, 2026
b2f1c6d
Update LCP calculation to use min function
lrvideckis Jan 26, 2026
4534728
Refactor suffix array sorting and LCP computation
lrvideckis Jan 26, 2026
a637445
Improve suffix array comparison handling
lrvideckis Jan 26, 2026
d282ec7
Add assertion for index 'a' in suffix array
lrvideckis Jan 26, 2026
d090096
Refactor conditionals for clarity in suffix_array_short.hpp
lrvideckis Jan 26, 2026
9a72891
Update suffix_array_short.hpp
lrvideckis Jan 26, 2026
8fd2384
fix
lrvideckis Jan 27, 2026
c073260
[auto-verifier] verify commit 8fd2384bbc4cab5f257615e5bb7d28512541961a
web-flow Jan 27, 2026
d263a75
fix
lrvideckis Jan 27, 2026
ae6f5ee
nameing nit
lrvideckis Jan 27, 2026
82db04b
fixes
lrvideckis Jan 27, 2026
c76e5c6
copy change here too
lrvideckis Jan 27, 2026
2cad993
fix
lrvideckis Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .verify-helper/timestamps.remote.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@
"tests/library_checker_aizu_tests/monotonic_stack_related/count_rectangles.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/monotonic_stack_related/max_rect_histogram.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/kmp.test.cpp": "2025-08-05 19:19:23 -0600",
"tests/library_checker_aizu_tests/strings/lcp_array.test.cpp": "2025-08-05 19:19:23 -0600",
"tests/library_checker_aizu_tests/strings/lcp_query_palindrome.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/lcp_query_zfunc.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/lcs_dp.test.cpp": "2025-08-05 19:19:23 -0600",
Expand All @@ -119,7 +118,6 @@
"tests/library_checker_aizu_tests/strings/sa_sort_pairs.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/single_matching_bs.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/suffix_array.test.cpp": "2026-01-18 11:15:41 +0000",
"tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp": "2025-08-05 19:19:23 -0600",
"tests/library_checker_aizu_tests/strings/trie.test.cpp": "2026-01-17 12:38:18 -0700",
"tests/library_checker_aizu_tests/strings/wildcard_pattern_matching.test.cpp": "2025-08-05 19:19:23 -0600",
"tests/library_checker_aizu_tests/trees/count_paths_per_length.test.cpp": "2025-12-11 21:47:53 +0000",
Expand Down
11 changes: 5 additions & 6 deletions library/strings/suffix_array/suffix_array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,13 @@ auto get_sa(const auto& s, int max_num) {
}
if (max_num == n) break;
}
int sz = 0;
int l = 0;
rep(i, 0, n) {
if (sz > 0) sz--;
if (l > 0) l--;
if (sa_inv[i] == 0) continue;
for (int j = sa[sa_inv[i] - 1];
max(i, j) + sz < n && s[i + sz] == s[j + sz];)
sz++;
lcp[sa_inv[i] - 1] = sz;
int j = sa[sa_inv[i] - 1];
while (max(i, j) + l < n && s[i + l] == s[j + l]) l++;
lcp[sa_inv[i] - 1] = l;
}
return tuple{sa, sa_inv, lcp};
}
30 changes: 17 additions & 13 deletions library/strings/suffix_array/suffix_array_short.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,35 @@
//! vi s_vec;
//! auto [sa1, sa_inv1, lcp1] = sa_short(s_vec);
//! @endcode
//! runs in ~1.5s for 5e5
//! runs in ~0.5s for 5e5
//! @time O(n * log^2(n))
//! @space O(n)
auto sa_short(const auto& s) {
int n = sz(s);
const int n = sz(s), b = 6;
vi sa(n), sa_inv(all(s)), lcp(n - 1);
iota(all(sa), 0);
for (int k = 1; k <= n; k *= 2) {
for (int j = 1; j <= n; j *= b) {
vi x(sa_inv);
auto proj = [&](int i) {
return pair(x[i], i + k < n ? x[i + k] : -1);
auto cmp = [&](int i1, int i2) {
rep(k, 0, b) {
int a = i1 + j * k < n ? x[i1 + j * k] : -1;
int b = i2 + j * k < n ? x[i2 + j * k] : -1;
if (a != b) return a < b;
}
return false;
};
ranges::sort(sa, {}, proj);
sort(all(sa), cmp);
sa_inv[sa[0]] = 0;
rep(i, 1, n) sa_inv[sa[i]] =
sa_inv[sa[i - 1]] + (proj(sa[i - 1]) != proj(sa[i]));
sa_inv[sa[i - 1]] + cmp(sa[i - 1], sa[i]);
}
int sz = 0;
int l = 0;
rep(i, 0, n) {
if (sz > 0) sz--;
if (l > 0) l--;
if (sa_inv[i] == 0) continue;
for (int j = sa[sa_inv[i] - 1];
max(i, j) + sz < n && s[i + sz] == s[j + sz];)
sz++;
lcp[sa_inv[i] - 1] = sz;
int j = sa[sa_inv[i] - 1];
while (max(i, j) + l < n && s[i + l] == s[j + l]) l++;
lcp[sa_inv[i] - 1] = l;
}
return tuple{sa, sa_inv, lcp};
}
2 changes: 1 addition & 1 deletion tests/.config/.cppcheck_suppression_list
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ syntaxError:../library/loops/supermasks.hpp:8
syntaxError:../library/math/prime_sieve/mobius.hpp:6
syntaxError:../library/trees/lca_rmq/iterate_subtree.hpp:6
knownConditionTrueFalse:../library/strings/suffix_array/suffix_array.hpp:62
knownConditionTrueFalse:../library/strings/suffix_array/suffix_array_short.hpp:29
knownConditionTrueFalse:../library/strings/suffix_array/suffix_array_short.hpp:34
knownConditionTrueFalse:../library/dsu/kruskal_tree.hpp:15
knownConditionTrueFalse:../library/dsu/dsu.hpp:11
constVariable:../kactl/content/numerical/NumberTheoreticTransform.h:30
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,22 @@ int main() {
cin.tie(0)->sync_with_stdio(0);
string s;
cin >> s;
auto [sa, sa_inv, _] = sa_short(vi(all(s)));
for (int i = 0; i < sz(s); i++) {
auto [sa, sa_inv, lcp] = sa_short(vi(all(s)));
rep(i, 0, sz(s)) {
assert(sa[sa_inv[i]] == i);
assert(sa_inv[sa[i]] == i);
}
vi lcp_kasai(sz(s) - 1);
int sz = 0;
rep(i, 0, sz(s)) {
if (sz > 0) sz--;
if (sa_inv[i] == 0) continue;
for (int j = sa[sa_inv[i] - 1];
max(i, j) + sz < sz(s) && s[i + sz] == s[j + sz];)
sz++;
lcp_kasai[sa_inv[i] - 1] = sz;
}
assert(lcp == lcp_kasai);
for (int val : sa) cout << val << " ";
cout << '\n';
}
3 changes: 0 additions & 3 deletions tests/scripts/grep_clangformat_cppcheck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ grep --extended-regexp "template\s?<typename" --recursive ../library/ && exit 1
echo "check 1 instead of true"
grep "true" --recursive ../library/ && exit 1

echo "check 0 instead of false"
grep "false" --recursive ../library/ && exit 1

echo "check ll instead of long long or int64_t"
grep "long long" --recursive ../library/ && exit 1
grep "int64_t" --recursive ../library/**/*.hpp | grep "uint64_t" --invert-match && exit 1
Expand Down
Loading