diff --git a/.verify-helper/timestamps.remote.json b/.verify-helper/timestamps.remote.json index 5192d41..e49549c 100644 --- a/.verify-helper/timestamps.remote.json +++ b/.verify-helper/timestamps.remote.json @@ -22,6 +22,7 @@ "test/atcoder-abc177-f.2.test.cpp": "2023-08-01 17:59:54 +0900", "test/atcoder-abc282-d.test.cpp": "2023-11-30 23:29:28 +0900", "test/atcoder-abc300-b.test.cpp": "2023-10-12 08:50:40 +0900", +"test/atcoder-abc331-f.test.cpp": "2024-02-12 01:35:20 +0900", "test/atcoder-edpc-g.test.cpp": "2023-11-30 23:29:28 +0900", "test/atcoder-past202012-n.test.cpp": "2023-08-01 18:34:30 +0900", "test/atcoder-past202012-n.test.py": "2023-09-07 14:26:13 +0900", @@ -29,7 +30,7 @@ "test/yosupo-convolution-mod-1000000007.test.cpp": "2023-09-16 00:07:15 +0900", "test/yosupo-convolution-mod-2-64.test.cpp": "2023-09-16 00:07:15 +0900", "test/yosupo-convolution-mod.test.cpp": "2023-09-16 00:07:15 +0900", -"test/yosupo-enumerate-palindromes.test.cpp": "2023-05-31 16:27:00 +0900", +"test/yosupo-enumerate-palindromes.test.cpp": "2024-02-12 01:35:20 +0900", "test/yosupo-enumerate-quotients.test.cpp": "2023-05-03 12:22:21 +0900", "test/yosupo-enumerate-quotients.test.py": "2023-05-23 13:25:17 +0900", "test/yosupo-lca.1.test.cpp": "2023-06-16 15:41:49 +0900", diff --git a/cpp/rolling-hash.hpp b/cpp/rolling-hash.hpp index 6874980..19037f9 100644 --- a/cpp/rolling-hash.hpp +++ b/cpp/rolling-hash.hpp @@ -3,6 +3,13 @@ #include #include #include +#include +#include +#include + +#include "traits.hpp" + +struct RHString; /** * @brief ローリングハッシュ @@ -94,4 +101,155 @@ class RollingHash { expand(r - l); return add(hash[r], MOD - mul(hash[l], power[r-l])); } + + friend RHString; +}; + +/** + * @brief ローリングハッシュによって管理される文字列型 + */ +struct RHString { + RollingHash& rh; + size_t sz; + unsigned long long hash1; //!< 正順 + unsigned long long hash2; //!< 逆順 + /** + * @brief コンストラクタ + * 予めRollingHashをインスタンス化しておく必要がある + */ + RHString(RollingHash& rh) : rh(rh), sz(0), hash1(0), hash2(0) {} + RHString(RollingHash& rh, size_t sz, unsigned long long hash1, unsigned long long hash2) : rh(rh), sz(sz), hash1(hash1), hash2(hash2) {} + RHString(const RHString& o) : rh(o.rh), sz(o.sz), hash1(o.hash1), hash2(o.hash2) {} + /** + * @brief vectorなどで初期化する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + RHString(RollingHash& rh, R&& v) : rh(rh) { + using std::begin, std::end, std::rbegin, std::rend; + sz = std::distance(begin(v), end(v)); + hash1 = rh.build(begin(v), end(v)).back(); + hash2 = rh.build(rbegin(v), rend(v)).back(); + } + /** + * @brief charやunsigned long longなどで初期化する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + RHString(RollingHash& rh, T&& x) : rh(rh) { + sz = 1; + hash1 = x; + hash2 = x; + } + /** + * @brief 文字列(string, const char*, string_view)で初期化する + */ + RHString(RollingHash& rh, std::string_view s) : rh(rh) { + sz = std::distance(s.begin(), s.end()); + hash1 = rh.build(s.begin(), s.end()).back(); + hash2 = rh.build(s.rbegin(), s.rend()).back(); + } + + /** + * @brief 回文か否か + */ + bool is_palindrome() const { + return hash1 == hash2; + } + size_t size() const { + return sz; + } + void clear() { + sz = 0; + hash1 = 0; + hash2 = 0; + } + bool empty() const { + return sz == 0; + } + RHString& operator+=(const RHString& o) { + assert(&rh == &o.rh); + rh.expand(sz); + rh.expand(o.sz); + hash1 = rh.add(rh.mul(hash1, rh.power[o.sz]), o.hash1); + hash2 = rh.add(hash2, rh.mul(o.hash2, rh.power[sz])); + sz += o.sz; + return *this; + } + /** + * @brief 再代入する + * RollingHashは同じものである必要がある + */ + void assign(const RHString& o) { + assert(&rh == &o.rh); + sz = o.sz; + hash1 = o.hash1; + hash2 = o.hash2; + } + /** + * @brief vectorなどを再代入する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + void assign(R&& v) { + using std::begin, std::end, std::rbegin, std::rend; + sz = std::distance(begin(v), end(v)); + hash1 = rh.build(begin(v), end(v)).back(); + hash2 = rh.build(rbegin(v), rend(v)).back(); + } + /** + * @brief charやunsigned long longなどを再代入する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + void assign(T&& x) { + sz = 1; + hash1 = x; + hash2 = x; + } + /** + * @brief 文字列(string, const char*, string_view)を再代入する + */ + void assign(std::string_view s) { + sz = std::distance(s.begin(), s.end()); + hash1 = rh.build(s.begin(), s.end()).back(); + hash2 = rh.build(s.rbegin(), s.rend()).back(); + } + /** + * @brief 再代入する + * RollingHashは同じものである必要がある + */ + RHString& operator=(const RHString& o) { + assign(o); + return *this; + } + /** + * @brief vectorなどを再代入する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + RHString& operator=(R&& v) { + assign(v); + return *this; + } + /** + * @brief charやunsigned long longなどを再代入する + */ + template && !std::is_convertible_v, std::nullptr_t> = nullptr> + RHString& operator=(T&& x) { + assign(x); + return *this; + } + /** + * @brief 文字列(string, const char*, string_view)を再代入する + */ + RHString& operator=(std::string_view s) { + assign(s); + return *this; + } + friend RHString operator+(const RHString& t1, const RHString& t2) { + RHString ret = t1; + ret += t2; + return ret; + } + friend bool operator==(const RHString& t1, const RHString& t2) { + assert(&t1.rh == &t2.rh); + return t1.sz == t2.sz && t1.hash1 == t2.hash1 && t1.hash2 == t2.hash2; + } + friend bool operator!=(const RHString& t1, const RHString& t2) { return !(t1 == t2); } }; diff --git a/cpp/traits.hpp b/cpp/traits.hpp new file mode 100644 index 0000000..590a8c5 --- /dev/null +++ b/cpp/traits.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +namespace detail { +using std::begin, std::end; + +template +struct is_range_impl : std::false_type {}; +template +struct is_range_impl()), end(std::declval()))>> : std::true_type {}; +} // namespace detail + +template +struct is_range : detail::is_range_impl::type {}; +template +inline constexpr bool is_range_v = is_range::value; diff --git a/test/atcoder-abc331-f.test.cpp b/test/atcoder-abc331-f.test.cpp new file mode 100644 index 0000000..1ad0e60 --- /dev/null +++ b/test/atcoder-abc331-f.test.cpp @@ -0,0 +1,37 @@ +#define PROBLEM "https://atcoder.jp/contests/abc331/tasks/abc331_f" + +#include + +#include "../cpp/rolling-hash.hpp" +#include "../cpp/segtree.hpp" + +RollingHash rh; +struct E { + RHString operator()() const { return RHString(rh); } +}; + +int main() { + int N, Q; + std::string S; + std::cin >> N >> Q >> S; + std::vector init; + init.reserve(N); + for (char c : S) { + init.emplace_back(rh, c); + } + StaticSegTree, E> seg(init); + while (Q--) { + int q; + std::cin >> q; + if (q == 1) { + int x; + char c; + std::cin >> x >> c; + seg.set(x - 1, RHString(rh, c)); + } else { + int L, R; + std::cin >> L >> R; + std::cout << (seg.prod(L - 1, R).is_palindrome() ? "Yes" : "No") << std::endl; + } + } +} \ No newline at end of file