Skip to main content
Back to problems
#1554
Medium Algorithms

Strings differ by one character

Hash Table String Rolling Hash Hash Function
40.0% acceptance
Mar 31, 2026
386
106

No description available.

Solution

Rust
Time O(n³)
Space O(n)
LeetCode
solution.rs
use std::collections::HashSet;

impl Solution {
  pub fn differ_by_one(dict: Vec<String>) -> bool {
    // Rolling hash approach: for each position, hash the string with that char replaced by wildcard
    let n = dict.len();
    if n == 0 { return false; }
    let m = dict[0].len();
    let modp: u64 = (1u64 << 61) - 1; // Mersenne prime
    let base: u64 = 31;
    
    // For each position j, create a hash that skips character at j
    // hash_without_j = hash - char_j * base^(m-1-j)
    // If two strings have the same hash_without_j for the same j, they differ by at most 1 char at position j
    
    let bytes: Vec<&[u8]> = dict.iter().map(|s| s.as_bytes()).collect();
    
    // Precompute powers of base
    let mut pow = vec![1u64; m + 1];
    for i in 1..=m {
      pow[i] = mul_mod(pow[i - 1], base, modp);
    }
    
    // Compute full hash for each string
    let mut full_hash = vec![0u64; n];
    for i in 0..n {
      let mut h = 0u64;
      for j in 0..m {
        h = add_mod(mul_mod(h, base, modp), (bytes[i][j] - b'a' + 1) as u64, modp);
      }
      full_hash[i] = h;
    }
    
    // For each position j, compute hash without position j and check for collisions
    for j in 0..m {
      let mut seen = HashSet::new();
      for i in 0..n {
        let char_contrib = mul_mod((bytes[i][j] - b'a' + 1) as u64, pow[m - 1 - j], modp);
        let h = sub_mod(full_hash[i], char_contrib, modp);
        if !seen.insert((h, j)) {
          // Verify: find the other string with same hash
          for k in 0..i {
            let char_contrib_k = mul_mod((bytes[k][j] - b'a' + 1) as u64, pow[m - 1 - j], modp);
            let hk = sub_mod(full_hash[k], char_contrib_k, modp);
            if hk == h {
              // Verify they actually differ by exactly 1
              let diffs = (0..m).filter(|&p| bytes[i][p] != bytes[k][p]).count();
              if diffs == 1 {
                return true;
              }
            }
          }
        }
      }
    }
    false
  }
}

fn mul_mod(a: u64, b: u64, m: u64) -> u64 {
  ((a as u128 * b as u128) % m as u128) as u64
}

fn add_mod(a: u64, b: u64, m: u64) -> u64 {
  let s = a + b;
  if s >= m { s - m } else { s }
}

fn sub_mod(a: u64, b: u64, m: u64) -> u64 {
  if a >= b { a - b } else { a + m - b }
}