|
3 | 3 | #include <string>
|
4 | 4 | #include <vector>
|
5 | 5 | #include <random>
|
| 6 | +#include <cassert> |
| 7 | +#include <string_view> |
| 8 | +#include <type_traits> |
| 9 | + |
| 10 | +#include "traits.hpp" |
| 11 | + |
| 12 | +struct RHString; |
6 | 13 |
|
7 | 14 | /**
|
8 | 15 | * @brief ローリングハッシュ
|
@@ -94,4 +101,155 @@ class RollingHash {
|
94 | 101 | expand(r - l);
|
95 | 102 | return add(hash[r], MOD - mul(hash[l], power[r-l]));
|
96 | 103 | }
|
| 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); } |
97 | 255 | };
|
0 commit comments