Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rhstring #102

Merged
merged 10 commits into from
Feb 28, 2024
3 changes: 2 additions & 1 deletion .verify-helper/timestamps.remote.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@
"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",
"test/yosupo-binomial-coefficient.test.cpp": "2023-09-16 00:25:06 +0900",
"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",
Expand Down
158 changes: 158 additions & 0 deletions cpp/rolling-hash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
#include <string>
#include <vector>
#include <random>
#include <cassert>
#include <string_view>
#include <type_traits>

#include "traits.hpp"

struct RHString;

/**
* @brief ローリングハッシュ
Expand Down Expand Up @@ -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 <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, 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 <class T, std::enable_if_t<std::is_convertible_v<T, unsigned long long> && !std::is_convertible_v<T, std::string_view>, 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 <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, 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 <class T, std::enable_if_t<std::is_convertible_v<T, unsigned long long> && !std::is_convertible_v<T, std::string_view>, 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 <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, std::nullptr_t> = nullptr>
RHString& operator=(R&& v) {
assign(v);
return *this;
}
/**
* @brief charやunsigned long longなどを再代入する
*/
template <class T, std::enable_if_t<std::is_convertible_v<T, unsigned long long> && !std::is_convertible_v<T, std::string_view>, 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); }
};
19 changes: 19 additions & 0 deletions cpp/traits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <iterator>
#include <type_traits>
#include <utility>

namespace detail {
using std::begin, std::end;

template <class T, class = void>
struct is_range_impl : std::false_type {};
template <class T>
struct is_range_impl<T, std::void_t<decltype(begin(std::declval<T&>()), end(std::declval<T&>()))>> : std::true_type {};
} // namespace detail

template <class T>
struct is_range : detail::is_range_impl<T>::type {};
template <class T>
inline constexpr bool is_range_v = is_range<T>::value;
37 changes: 37 additions & 0 deletions test/atcoder-abc331-f.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#define PROBLEM "https://atcoder.jp/contests/abc331/tasks/abc331_f"

#include <iostream>

#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<RHString> init;
init.reserve(N);
for (char c : S) {
init.emplace_back(rh, c);
}
StaticSegTree<RHString, std::plus<RHString>, 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;
}
}
}
Loading