Skip to content

Commit

Permalink
Merge pull request #102 from rainbou-kpr/rhstring
Browse files Browse the repository at this point in the history
add rhstring
  • Loading branch information
KowerKoint authored Feb 28, 2024
2 parents 5b722d1 + dab23a4 commit 476e5dd
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 1 deletion.
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-exp-of-fps.test.cpp": "2024-01-20 00:58:37 +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;
}
}
}

0 comments on commit 476e5dd

Please sign in to comment.