結果

問題 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
権限があれば一括ダウンロードができます

ソースコード

diff #

#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;
}
0