diff --git a/.verify-helper/timestamps.remote.json b/.verify-helper/timestamps.remote.json index 888d63a0..76db5bf5 100644 --- a/.verify-helper/timestamps.remote.json +++ b/.verify-helper/timestamps.remote.json @@ -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", @@ -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", diff --git a/library/strings/suffix_array/suffix_array.hpp b/library/strings/suffix_array/suffix_array.hpp index 8ce1278f..cdeda4ec 100644 --- a/library/strings/suffix_array/suffix_array.hpp +++ b/library/strings/suffix_array/suffix_array.hpp @@ -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}; } diff --git a/library/strings/suffix_array/suffix_array_short.hpp b/library/strings/suffix_array/suffix_array_short.hpp index cb0a7f34..4bfcc087 100644 --- a/library/strings/suffix_array/suffix_array_short.hpp +++ b/library/strings/suffix_array/suffix_array_short.hpp @@ -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}; } diff --git a/tests/.config/.cppcheck_suppression_list b/tests/.config/.cppcheck_suppression_list index 21d6bced..fbdc851f 100644 --- a/tests/.config/.cppcheck_suppression_list +++ b/tests/.config/.cppcheck_suppression_list @@ -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 diff --git a/tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp b/tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp index bb25b839..59685667 100644 --- a/tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp +++ b/tests/library_checker_aizu_tests/strings/suffix_array_short.test.cpp @@ -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'; } diff --git a/tests/scripts/grep_clangformat_cppcheck.sh b/tests/scripts/grep_clangformat_cppcheck.sh index 56c9863f..90f6fdb4 100755 --- a/tests/scripts/grep_clangformat_cppcheck.sh +++ b/tests/scripts/grep_clangformat_cppcheck.sh @@ -16,9 +16,6 @@ grep --extended-regexp "template\s?