Skip to content

Commit 476e5dd

Browse files
authored
Merge pull request #102 from rainbou-kpr/rhstring
add rhstring
2 parents 5b722d1 + dab23a4 commit 476e5dd

File tree

4 files changed

+216
-1
lines changed

4 files changed

+216
-1
lines changed

.verify-helper/timestamps.remote.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222
"test/atcoder-abc177-f.2.test.cpp": "2023-08-01 17:59:54 +0900",
2323
"test/atcoder-abc282-d.test.cpp": "2023-11-30 23:29:28 +0900",
2424
"test/atcoder-abc300-b.test.cpp": "2023-10-12 08:50:40 +0900",
25+
"test/atcoder-abc331-f.test.cpp": "2024-02-12 01:35:20 +0900",
2526
"test/atcoder-edpc-g.test.cpp": "2023-11-30 23:29:28 +0900",
2627
"test/atcoder-past202012-n.test.cpp": "2023-08-01 18:34:30 +0900",
2728
"test/atcoder-past202012-n.test.py": "2023-09-07 14:26:13 +0900",
2829
"test/yosupo-binomial-coefficient.test.cpp": "2023-09-16 00:25:06 +0900",
2930
"test/yosupo-convolution-mod-1000000007.test.cpp": "2023-09-16 00:07:15 +0900",
3031
"test/yosupo-convolution-mod-2-64.test.cpp": "2023-09-16 00:07:15 +0900",
3132
"test/yosupo-convolution-mod.test.cpp": "2023-09-16 00:07:15 +0900",
32-
"test/yosupo-enumerate-palindromes.test.cpp": "2023-05-31 16:27:00 +0900",
33+
"test/yosupo-enumerate-palindromes.test.cpp": "2024-02-12 01:35:20 +0900",
3334
"test/yosupo-enumerate-quotients.test.cpp": "2023-05-03 12:22:21 +0900",
3435
"test/yosupo-enumerate-quotients.test.py": "2023-05-23 13:25:17 +0900",
3536
"test/yosupo-exp-of-fps.test.cpp": "2024-01-20 00:58:37 +0900",

cpp/rolling-hash.hpp

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
#include <string>
44
#include <vector>
55
#include <random>
6+
#include <cassert>
7+
#include <string_view>
8+
#include <type_traits>
9+
10+
#include "traits.hpp"
11+
12+
struct RHString;
613

714
/**
815
* @brief ローリングハッシュ
@@ -94,4 +101,155 @@ class RollingHash {
94101
expand(r - l);
95102
return add(hash[r], MOD - mul(hash[l], power[r-l]));
96103
}
104+
105+
friend RHString;
106+
};
107+
108+
/**
109+
* @brief ローリングハッシュによって管理される文字列型
110+
*/
111+
struct RHString {
112+
RollingHash& rh;
113+
size_t sz;
114+
unsigned long long hash1; //!< 正順
115+
unsigned long long hash2; //!< 逆順
116+
/**
117+
* @brief コンストラクタ
118+
* 予めRollingHashをインスタンス化しておく必要がある
119+
*/
120+
RHString(RollingHash& rh) : rh(rh), sz(0), hash1(0), hash2(0) {}
121+
RHString(RollingHash& rh, size_t sz, unsigned long long hash1, unsigned long long hash2) : rh(rh), sz(sz), hash1(hash1), hash2(hash2) {}
122+
RHString(const RHString& o) : rh(o.rh), sz(o.sz), hash1(o.hash1), hash2(o.hash2) {}
123+
/**
124+
* @brief vectorなどで初期化する
125+
*/
126+
template <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, std::nullptr_t> = nullptr>
127+
RHString(RollingHash& rh, R&& v) : rh(rh) {
128+
using std::begin, std::end, std::rbegin, std::rend;
129+
sz = std::distance(begin(v), end(v));
130+
hash1 = rh.build(begin(v), end(v)).back();
131+
hash2 = rh.build(rbegin(v), rend(v)).back();
132+
}
133+
/**
134+
* @brief charやunsigned long longなどで初期化する
135+
*/
136+
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>
137+
RHString(RollingHash& rh, T&& x) : rh(rh) {
138+
sz = 1;
139+
hash1 = x;
140+
hash2 = x;
141+
}
142+
/**
143+
* @brief 文字列(string, const char*, string_view)で初期化する
144+
*/
145+
RHString(RollingHash& rh, std::string_view s) : rh(rh) {
146+
sz = std::distance(s.begin(), s.end());
147+
hash1 = rh.build(s.begin(), s.end()).back();
148+
hash2 = rh.build(s.rbegin(), s.rend()).back();
149+
}
150+
151+
/**
152+
* @brief 回文か否か
153+
*/
154+
bool is_palindrome() const {
155+
return hash1 == hash2;
156+
}
157+
size_t size() const {
158+
return sz;
159+
}
160+
void clear() {
161+
sz = 0;
162+
hash1 = 0;
163+
hash2 = 0;
164+
}
165+
bool empty() const {
166+
return sz == 0;
167+
}
168+
RHString& operator+=(const RHString& o) {
169+
assert(&rh == &o.rh);
170+
rh.expand(sz);
171+
rh.expand(o.sz);
172+
hash1 = rh.add(rh.mul(hash1, rh.power[o.sz]), o.hash1);
173+
hash2 = rh.add(hash2, rh.mul(o.hash2, rh.power[sz]));
174+
sz += o.sz;
175+
return *this;
176+
}
177+
/**
178+
* @brief 再代入する
179+
* RollingHashは同じものである必要がある
180+
*/
181+
void assign(const RHString& o) {
182+
assert(&rh == &o.rh);
183+
sz = o.sz;
184+
hash1 = o.hash1;
185+
hash2 = o.hash2;
186+
}
187+
/**
188+
* @brief vectorなどを再代入する
189+
*/
190+
template <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, std::nullptr_t> = nullptr>
191+
void assign(R&& v) {
192+
using std::begin, std::end, std::rbegin, std::rend;
193+
sz = std::distance(begin(v), end(v));
194+
hash1 = rh.build(begin(v), end(v)).back();
195+
hash2 = rh.build(rbegin(v), rend(v)).back();
196+
}
197+
/**
198+
* @brief charやunsigned long longなどを再代入する
199+
*/
200+
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>
201+
void assign(T&& x) {
202+
sz = 1;
203+
hash1 = x;
204+
hash2 = x;
205+
}
206+
/**
207+
* @brief 文字列(string, const char*, string_view)を再代入する
208+
*/
209+
void assign(std::string_view s) {
210+
sz = std::distance(s.begin(), s.end());
211+
hash1 = rh.build(s.begin(), s.end()).back();
212+
hash2 = rh.build(s.rbegin(), s.rend()).back();
213+
}
214+
/**
215+
* @brief 再代入する
216+
* RollingHashは同じものである必要がある
217+
*/
218+
RHString& operator=(const RHString& o) {
219+
assign(o);
220+
return *this;
221+
}
222+
/**
223+
* @brief vectorなどを再代入する
224+
*/
225+
template <class R, std::enable_if_t<is_range_v<R> && !std::is_convertible_v<R, std::string_view>, std::nullptr_t> = nullptr>
226+
RHString& operator=(R&& v) {
227+
assign(v);
228+
return *this;
229+
}
230+
/**
231+
* @brief charやunsigned long longなどを再代入する
232+
*/
233+
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>
234+
RHString& operator=(T&& x) {
235+
assign(x);
236+
return *this;
237+
}
238+
/**
239+
* @brief 文字列(string, const char*, string_view)を再代入する
240+
*/
241+
RHString& operator=(std::string_view s) {
242+
assign(s);
243+
return *this;
244+
}
245+
friend RHString operator+(const RHString& t1, const RHString& t2) {
246+
RHString ret = t1;
247+
ret += t2;
248+
return ret;
249+
}
250+
friend bool operator==(const RHString& t1, const RHString& t2) {
251+
assert(&t1.rh == &t2.rh);
252+
return t1.sz == t2.sz && t1.hash1 == t2.hash1 && t1.hash2 == t2.hash2;
253+
}
254+
friend bool operator!=(const RHString& t1, const RHString& t2) { return !(t1 == t2); }
97255
};

cpp/traits.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
3+
#include <iterator>
4+
#include <type_traits>
5+
#include <utility>
6+
7+
namespace detail {
8+
using std::begin, std::end;
9+
10+
template <class T, class = void>
11+
struct is_range_impl : std::false_type {};
12+
template <class T>
13+
struct is_range_impl<T, std::void_t<decltype(begin(std::declval<T&>()), end(std::declval<T&>()))>> : std::true_type {};
14+
} // namespace detail
15+
16+
template <class T>
17+
struct is_range : detail::is_range_impl<T>::type {};
18+
template <class T>
19+
inline constexpr bool is_range_v = is_range<T>::value;

test/atcoder-abc331-f.test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#define PROBLEM "https://atcoder.jp/contests/abc331/tasks/abc331_f"
2+
3+
#include <iostream>
4+
5+
#include "../cpp/rolling-hash.hpp"
6+
#include "../cpp/segtree.hpp"
7+
8+
RollingHash rh;
9+
struct E {
10+
RHString operator()() const { return RHString(rh); }
11+
};
12+
13+
int main() {
14+
int N, Q;
15+
std::string S;
16+
std::cin >> N >> Q >> S;
17+
std::vector<RHString> init;
18+
init.reserve(N);
19+
for (char c : S) {
20+
init.emplace_back(rh, c);
21+
}
22+
StaticSegTree<RHString, std::plus<RHString>, E> seg(init);
23+
while (Q--) {
24+
int q;
25+
std::cin >> q;
26+
if (q == 1) {
27+
int x;
28+
char c;
29+
std::cin >> x >> c;
30+
seg.set(x - 1, RHString(rh, c));
31+
} else {
32+
int L, R;
33+
std::cin >> L >> R;
34+
std::cout << (seg.prod(L - 1, R).is_palindrome() ? "Yes" : "No") << std::endl;
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)