結果
| 問題 |
No.1909 Detect from Substrings
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:09:15 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 7,880 bytes |
| コンパイル時間 | 1,628 ms |
| コンパイル使用メモリ | 111,148 KB |
| 実行使用メモリ | 238,072 KB |
| 最終ジャッジ日時 | 2025-05-14 13:10:24 |
| 合計ジャッジ時間 | 6,044 ms |
|
ジャッジサーバーID (参考情報) |
judge2 / judge5 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 1 TLE * 2 -- * 33 |
ソースコード
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map> // Using unordered_map for potentially better performance
#include <unordered_set> // Using unordered_set for potentially better performance
#include <utility> // For std::pair
#include <functional> // For std::hash
using namespace std;
// Use long long for hash values to avoid overflow with large moduli
typedef long long ll;
// Define two large prime moduli for double hashing. This reduces collision probability.
const ll P1 = 1e9 + 7;
const ll P2 = 1e9 + 9;
// Define two bases for the polynomial rolling hash. Small prime bases are common.
// Using different bases for the two hash functions further reduces collisions.
ll B1 = 31;
ll B2 = 37;
// Function for modular exponentiation (calculates base^exp % modulus)
// Used to compute powers of bases B1 and B2 efficiently.
ll power(ll base, ll exp, ll modulus) {
ll res = 1;
base %= modulus;
while (exp > 0) {
if (exp % 2 == 1) res = (res * base) % modulus; // If exponent is odd, multiply result with base
base = (base * base) % modulus; // Square the base
exp /= 2; // Halve the exponent
}
return res;
}
// Precompute powers of bases B1 and B2 up to max_len.
// This allows O(1) lookup for B^k needed in hash calculations.
vector<ll> B1_pow, B2_pow;
void precompute_powers(int max_len) {
B1_pow.resize(max_len + 1);
B2_pow.resize(max_len + 1);
B1_pow[0] = 1; // B^0 = 1
B2_pow[0] = 1;
for (int i = 1; i <= max_len; ++i) {
B1_pow[i] = (B1_pow[i - 1] * B1) % P1; // B^i = B^{i-1} * B
B2_pow[i] = (B2_pow[i - 1] * B2) % P2;
}
}
// Structure to compute and store prefix hashes for a given string `s`.
// This allows efficient calculation of substring hashes.
struct PrefixHashes {
vector<ll> h1, h2; // Store prefix hashes for both hash functions
int len; // Length of the string
PrefixHashes(const string& s) {
len = s.length();
h1.resize(len + 1, 0); // h1[i] stores hash of s[0..i-1] (or s[1..i] if 1-indexed)
h2.resize(len + 1, 0);
for (int i = 0; i < len; ++i) {
// Calculate prefix hash iteratively: H(S[1..i]) = H(S[1..i-1]) * B + val(S[i])
// Using 1-based character values ('a' -> 1, 'b' -> 2, ...) avoids issues with character 'a' mapping to 0.
h1[i + 1] = (h1[i] * B1 + (s[i] - 'a' + 1)) % P1;
h2[i + 1] = (h2[i] * B2 + (s[i] - 'a' + 1)) % P2;
}
}
// Get hash of the suffix starting at index k (1-based index). S[k..len]
pair<ll, ll> get_suffix_hash(int k) {
if (k > len) return {0, 0}; // If k is out of bounds, suffix is empty, hash is 0.
ll suffix_len = len - k + 1;
// Calculate hash of substring S[k..len] using prefix hashes:
// H(S[k..len]) = (H(S[1..len]) - H(S[1..k-1]) * B^{len - k + 1}) mod P
// Need careful modular subtraction: (a - b + P) % P to handle potential negative results.
ll H1 = (h1[len] - (h1[k - 1] * B1_pow[suffix_len]) % P1 + P1) % P1;
ll H2 = (h2[len] - (h2[k - 1] * B2_pow[suffix_len]) % P2 + P2) % P2;
return {H1, H2};
}
// Get hash of the prefix ending at index k-1 (1-based index). S[1..k-1]
pair<ll, ll> get_prefix_hash(int k_minus_1) {
// Accessing h1[k-1], h2[k-1] directly gives hash of S[1..k-1].
// If k=1, k_minus_1=0. Returns {h1[0], h2[0]} = {0, 0}, correct for empty prefix.
return {h1[k_minus_1], h2[k_minus_1]};
}
};
// Custom hash function for pairs of long longs, needed for unordered_map/set keys.
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Combine the two hash values. A common method used in Boost library.
// Simple XOR `h1 ^ h2` might have higher collision rates.
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
}
};
int main() {
// Faster I/O operations
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, M;
cin >> N >> M; // Read number of strings N and length M
vector<string> S(N);
for (int i = 0; i < N; ++i) {
cin >> S[i]; // Read the N strings
}
// Precompute powers of bases up to M+1, as the target string T has length M+1.
precompute_powers(M + 1);
// Map to store counts of each generated hash pair (representing a unique string T).
// Key: pair of hash values (h1, h2). Value: count of how many S_i generated this T.
unordered_map<pair<ll, ll>, int, pair_hash> counts;
// Process each input string S_i
for (int i = 0; i < N; ++i) {
PrefixHashes ph(S[i]); // Compute prefix hashes for S_i
// Set to keep track of hash pairs generated from the current S_i.
// This ensures that if multiple insertions into S_i produce the same T,
// we only increment the count for T once for this S_i.
unordered_set<pair<ll, ll>, pair_hash> visited_hashes;
// Iterate through all possible insertion positions k (1 to M+1)
// k=1 means insert before the first character. k=M+1 means insert after the last character.
for (int k = 1; k <= M + 1; ++k) {
// Iterate through all possible characters 'a' through 'z' to insert
for (char c = 'a'; c <= 'z'; ++c) {
int char_val = c - 'a' + 1; // 1-based value for the character
// Get hash of prefix S[1..k-1] (denoted P)
pair<ll, ll> prefix_hash = ph.get_prefix_hash(k - 1);
// Get hash of suffix S[k..M] (denoted S')
pair<ll, ll> suffix_hash = ph.get_suffix_hash(k);
ll H1_prefix = prefix_hash.first;
ll H2_prefix = prefix_hash.second;
ll H1_suffix = suffix_hash.first;
ll H2_suffix = suffix_hash.second;
// Calculate length of the suffix S'. If k=M+1, suffix is empty, length is 0.
int suffix_len = M - k + 1;
// Calculate hash of the string T formed by inserting c at position k: T = P c S'
// Use formula: H(T) = ( H(P c) * B^{|S'|} + H(S') ) mod P
// First calculate H(P c) = ( H(P) * B + val(c) ) mod P
ll H1_Pc = (H1_prefix * B1 + char_val) % P1;
ll H2_Pc = (H2_prefix * B2 + char_val) % P2;
// Then combine H(Pc) with H(S')
ll final_H1 = (H1_Pc * B1_pow[suffix_len] + H1_suffix) % P1;
ll final_H2 = (H2_Pc * B2_pow[suffix_len] + H2_suffix) % P2;
// The resulting hash pair for string T
pair<ll, ll> current_hash = {final_H1, final_H2};
// Check if this hash pair has already been generated from the current S_i
if (visited_hashes.find(current_hash) == visited_hashes.end()) {
// If not visited for S_i, increment its count in the global map
counts[current_hash]++;
// Mark this hash pair as visited for the current S_i
visited_hashes.insert(current_hash);
}
}
}
}
int ans = 0; // Initialize the answer counter
// Iterate through the map of hash counts
for (auto const& [hash_pair, count] : counts) {
// If a hash pair (representing a string T) has a count equal to N,
// it means T was generated from all N input strings S_i.
// Such T is a valid solution.
if (count == N) {
ans++; // Increment the answer count
}
}
// Output the final count of valid T strings
cout << ans << endl;
return 0;
}
qwewe