#define ROLLING_HASH_MAX_LENGTH 50000 //! @file rolling_hash.hpp //! @note The implementation is based on this article https://qiita.com/keymoon/items/11fac5627672a6d6a9f6 #ifndef ROLLING_HASH_HPP # define ROLLING_HASH_HPP # ifndef ROLLING_HASH_MAX_LENGTH # define ROLLING_HASH_MAX_LENGTH 200000 # define ROLLING_HASH_MAX_LENGTH_not_defined # endif # include # include # include # include # include # include # include # include # include # ifndef O_assert //! @brief Assert macro # define O_assert(...) assert(__VA_ARGS__) # define O_assert_not_defined # endif # ifndef err_and_exit # if (CP_LIBRARY_DEBUG_LEVEL >= 2) //! @brief Print error message and exit # define err_and_exit(...) \ do { \ __VA_ARGS__ \ O_assert(false); \ } while (false) # else # define err_and_exit(msg) (static_cast(0)) # endif # define err_and_exit_not_defined # endif namespace lib { namespace internal { using u64 = std::uint_least64_t; template constexpr u64 mask = (u64(1) << index) - 1; template constexpr u64 pad = mask* mask::digits - index>; [[gnu::const]] [[nodiscard]] constexpr u64 mod_mersenne_61(const u64 x) noexcept { if (const u64 res = (x >> 61) + (x & mask<61>); res >= mask<61>) return res - mask<61>; else return res; } [[gnu::const]] [[nodiscard]] constexpr u64 mult(const u64 x, const u64 y) noexcept { const u64 x_upper = (x >> 31); const u64 x_lower = (x & mask<31>); const u64 y_upper = (y >> 31); const u64 y_lower = (y & mask<31>); const u64 z = x_upper * y_lower + x_lower * y_upper; return ((x_upper * y_upper) << 1) + (z >> 30) + ((z & mask<30>) << 31) + (x_lower * y_lower); } template [[nodiscard]] u64 pow_mod_mersenne_61(unsigned index) { static bool first = true; static std::array res; if (__builtin_expect(first, 0)) { first = false; res[0] = 1; for (unsigned i = 1; i <= ROLLING_HASH_MAX_LENGTH; ++i) res[i] = mod_mersenne_61(mult(res[i - 1], base)); } return res[index]; } template void print_tuple(const TupleType& arg, std::ostream& os, std::index_sequence) { static_cast(((os << (Is == 0 ? "" : ", "), (os << std::get(arg))), ...)); } // Using this type alias unables GCC to compile the code for some reason. (no problem with Clang/MSVC. maybe a bug.) // template // using hash_t = decltype(std::tuple {(static_cast(Bases))...}); // See: https://wandbox.org/permlink/xpqiYobUkI1EGpgo (on GCC 12.0.0) // https://wandbox.org/permlink/26ZTzuIfTivSz2lR (on Clang 13.0.0) // ToDo: Replace decltype(std::tuple {(static_cast(Bases))...}) with hash_t // and replace decltype(std::tuple {(static_cast(Bases))...}) with internal::hash_t // when we figure out the workaround or this bug? is fixed. template void build_hash_sequence_impl(const Container& src, std::vector(Bases))...})>& dest, std::index_sequence) { constexpr std::tuple bases_tuple = std::tuple {Bases...}; const std::size_t size = std::size(src); auto iter = std::cbegin(src); for (std::size_t i = 0; i < size; ++iter, ++i) static_cast(((std::get(dest[i + 1]) = mod_mersenne_61(mult(std::get(dest[i]), std::get(bases_tuple)) + static_cast(*iter))), ...)); } template [[nodiscard]] std::vector(Bases))...})> build_hash_sequence(const Container& src) { std::vector(Bases))...})> res(std::size(src) + 1); build_hash_sequence_impl(src, res, std::make_index_sequence {}); return res; } template [[nodiscard]] constexpr decltype(std::tuple {(static_cast(Bases))...}) substr_hash_impl(const std::vector(Bases))...})>& hashes, const int starts, const int length, std::index_sequence) { constexpr std::tuple bases_tuple = std::tuple {Bases...}; decltype(std::tuple {(static_cast(Bases))...}) res; static_cast(((std::get(res) = mod_mersenne_61(std::get(hashes[starts + length]) + pad<61> - mod_mersenne_61(mult(std::get(hashes[starts]), pow_mod_mersenne_61(bases_tuple)>(length))))), ...)); return res; } template [[nodiscard]] constexpr decltype(std::tuple {(static_cast(Bases))...}) substr_hash(const std::vector(Bases))...})>& hashes, const int starts, const int length) { return substr_hash_impl(hashes, starts, length, std::make_index_sequence {}); } } // namespace internal # if (CP_LIBRARY_DEBUG_LEVEL >= 2) template class rolling_hash { private: std::vector src; std::vector(Bases))...})> hashes; struct single_hash { private: std::vector src; decltype(std::tuple {(static_cast(Bases))...}) val; int length; public: constexpr single_hash() noexcept : val(), length() {} single_hash(const std::vector& source, const decltype(std::tuple {(static_cast(Bases))...})& hash, const int size) : src(source), val(hash), length(size) {} single_hash(std::vector&& source, decltype(std::tuple {(static_cast(Bases))...})&& hash, const int size) : src(std::move(source)), val(std::move(hash)), length(size) {} [[nodiscard]] int size() const noexcept { return length; } [[nodiscard]] bool operator==(const single_hash& rhs) const { const bool res = (val == rhs.val); const bool precise_res = (src == rhs.src); if (__builtin_expect(res != precise_res, 0)) { err_and_exit( const std::ios_base::fmtflags prev_state = std::cerr.flags(); std::cerr << "*** Hash collision detected ***\n" << "Hash value: (" << std::hex; internal::print_tuple(val, std::cerr, std::make_index_sequence> {}); std::cerr << ")\n\n"; std::cerr.flags(prev_state); if constexpr (std::is_same_v) { std::cerr << "String 1: \""; std::for_each(std::cbegin(src), std::cend(src), [](const char c) { std::cerr << c; }); std::cerr << "\"\n"; std::cerr << "String 2: \""; std::for_each(std::cbegin(rhs.src), std::cend(rhs.src), [](const char c) { std::cerr << c; }); std::cerr << "\"\n\n"; } else { std::cerr << "Sequence 1: [ "; std::for_each(std::cbegin(src), std::cend(src), [](const Elem& e) { std::cerr << e << ' '; }); std::cerr << "]\n"; std::cerr << "Sequence 2: [ "; std::for_each(std::cbegin(rhs.src), std::cend(rhs.src), [](const Elem& e) { std::cerr << e << ' '; }); std::cerr << "]\n\n"; }); } return res; } [[nodiscard]] bool operator!=(const single_hash& rhs) const { return !(*this == rhs); } [[nodiscard]] auto hash_value() const noexcept { return val; } void debug_print(const std::string& name = "", std::ostream& os = std::cerr) const { if (!name.empty()) os << name << ": "; if constexpr (std::is_same_v) { os << " String = \""; std::for_each(std::cbegin(src), std::cend(src), [&](const char c) { os << c; }); os << "\"\n"; } else { os << " Sequence = [ "; std::for_each(std::cbegin(src), std::cend(src), [&](const Elem& e) { os << e << ' '; }); os << "]\n"; } const std::ios_base::fmtflags prev_state = os.flags(); os << (name.empty() ? std::string() : std::string(std::size(name) + 2, ' ')) << "Hash value = " << std::hex << '('; internal::print_tuple(val, os, std::make_index_sequence> {}); os << ")\n"; os.flags(prev_state); } }; public: template rolling_hash(const Container& source) : src(std::cbegin(source), std::cend(source)), hashes(internal::build_hash_sequence(src)) {} [[nodiscard]] int size() const noexcept(noexcept(std::size(src))) { return static_cast(std::size(src)); } [[nodiscard]] single_hash whole_string() const { return single_hash(src, hashes.back(), size()); } [[nodiscard]] single_hash substring(const int starts, int length = std::numeric_limits::max()) const { length = std::min(static_cast(std::size(src)) - starts, length); return single_hash(std::vector(std::cbegin(src) + starts, std::cbegin(src) + starts + length), internal::substr_hash(hashes, starts, length), length); } }; # else template class rolling_hash { private: std::vector(Bases))...})> hashes; struct single_hash { private: decltype(std::tuple {(static_cast(Bases))...}) val; int length; public: constexpr single_hash() noexcept : val(), length() {} constexpr single_hash(const decltype(std::tuple {(static_cast(Bases))...})& hash, const int size) : val(hash), length(size) {} constexpr single_hash(decltype(std::tuple {(static_cast(Bases))...})&& hash, const int size) : val(std::move(hash)), length(size) {} [[nodiscard]] constexpr int size() const noexcept { return length; } [[nodiscard]] constexpr bool operator==(const single_hash& rhs) const noexcept { return (length == rhs.length) && (val == rhs.val); } [[nodiscard]] constexpr bool operator!=(const single_hash& rhs) const noexcept { return !(*this == rhs); } [[nodiscard]] constexpr auto hash_value() const noexcept { return val; } void debug_print([[maybe_unused]] const std::string& name = "", [[maybe_unused]] std::ostream& os = std::cerr) const { # if (CP_LIBRARY_DEBUG_LEVEL >= 1) if (!name.empty()) os << name << ": "; const std::ios_base::fmtflags prev_state = os.flags(); os << "Hash value = " << std::hex << '('; internal::print_tuple(val, os, std::make_index_sequence> {}); os << ")\n"; os.flags(prev_state); # endif } }; public: template rolling_hash(const Container& source) : hashes(internal::build_hash_sequence(source)) {} [[nodiscard]] int size() const noexcept(noexcept(std::size(hashes))) { return static_cast(std::size(hashes)) - 1; } [[nodiscard]] single_hash whole_string() const { return single_hash(hashes.back(), size()); } [[nodiscard]] single_hash substring(const int starts, int length = std::numeric_limits::max()) const { length = std::min(static_cast(std::size(hashes)) - starts - 1, length); return single_hash(internal::substr_hash(hashes, starts, length), length); } }; # endif template [[nodiscard]] auto get_rolling_hash(const Container& src) { return rolling_hash()))>, 436523069, 681531337, 843203861>(src); } template [[nodiscard]] auto get_single_hash(const Container& src) { return get_rolling_hash(src).whole_string(); } template using rolling_hash_t = decltype(get_rolling_hash(std::declval())); template using single_hash_t = decltype(get_single_hash(std::declval())); } // namespace lib # ifdef err_and_exit_not_defined # undef err_and_exit # undef err_and_exit_not_defined # endif # ifdef O_assert_not_defined # undef O_assert # undef O_assert_not_defined # endif # ifdef ROLLING_HASH_MAX_LENGTH_not_defined # undef ROLLING_HASH_MAX_LENGTH # undef ROLLING_HASH_MAX_LENGTH_not_defined # endif #endif // ROLLING_HASH_HPP #include #include int main() { std::ios_base::sync_with_stdio(false); std::cin.tie(nullptr); std::string S; std::cin >> S; const auto S_hashes = lib::get_rolling_hash(S); const auto S_len = static_cast(std::size(S)); std::vector precalc_hashes(S_len, std::vector>(11)); for (int len = 1; len <= 10; ++len) for (int start = 0; start <= S_len - len; ++start) precalc_hashes[start][len] = S_hashes.substring(start, len); int M, res = 0; std::cin >> M; std::string tmp; while (M--) { std::cin >> tmp; const auto tmp_hash = lib::get_single_hash(tmp); const auto tmp_len = static_cast(std::size(tmp)); std::cerr << tmp << '\n'; for (int start = 0; start <= S_len - tmp_len; ++start) res += static_cast(precalc_hashes[start][tmp_len] == tmp_hash); } std::cout << res << '\n'; }