Bird
Raised Fist0
DSA Rustprogramming~3 mins

Why KMP Pattern Matching Algorithm in DSA Rust?

Choose your learning style10 modes available
The Big Idea

What if you could find a phrase in a huge book without ever looking back twice?

The Scenario

Imagine you have a huge book and you want to find every place where a certain phrase appears. You try to look for the phrase by checking each word one by one from the start every time you find a mismatch.

The Problem

This manual way is very slow and tiring because you keep going back and checking the same words again and again. It wastes a lot of time and can easily cause mistakes if you lose track.

The Solution

The KMP Pattern Matching Algorithm helps by remembering where to continue checking after a mismatch, so you never check the same part twice. It makes searching fast and smooth, even in very long texts.

Before vs After
✗ Before
fn search(text: &str, pattern: &str) {
    let n = text.len();
    let m = pattern.len();
    let end = n.saturating_sub(m).saturating_add(1);
    for i in 0..end {
        if &text[i..i + m] == pattern {
            println!("Found at {}", i);
        }
    }
}
✓ After
fn compute_lps(pattern: &str) -> Vec<usize> {
    let pi = pattern.as_bytes();
    let m = pi.len();
    let mut lps = vec![0; m];
    let mut j = 0;
    for i in 1..m {
        while j > 0 && pi[i] != pi[j] {
            j = lps[j - 1];
        }
        if pi[i] == pi[j] {
            j += 1;
        }
        lps[i] = j;
    }
    lps
}

fn kmp_search(text: &str, pattern: &str) {
    if pattern.is_empty() {
        return;
    }
    let lps = compute_lps(pattern);
    let mut i = 0;
    let mut j = 0;
    let text_bytes = text.as_bytes();
    let pattern_bytes = pattern.as_bytes();
    while i < text_bytes.len() {
        if text_bytes[i] == pattern_bytes[j] {
            i += 1;
            j += 1;
            if j == pattern_bytes.len() {
                println!("Found at {}", i - j);
                j = lps[j - 1];
            }
        } else if j > 0 {
            j = lps[j - 1];
        } else {
            i += 1;
        }
    }
}
What It Enables

It enables lightning-fast searching of patterns inside huge texts without repeating work.

Real Life Example

Search engines use this to quickly find your search words inside billions of web pages.

Key Takeaways

Manual searching repeats checks and wastes time.

KMP remembers where to continue after mismatches.

KMP makes pattern searching efficient and reliable.

Practice

(1/5)
1. What is the main purpose of the KMP Pattern Matching Algorithm?
easy
A. To find occurrences of a pattern string within a text efficiently
B. To sort an array of numbers quickly
C. To reverse a string in linear time
D. To calculate the factorial of a number

Solution

  1. Step 1: Understand the problem KMP solves

    KMP is designed to search for a pattern inside a larger text efficiently without rechecking characters unnecessarily.
  2. Step 2: Compare with other options

    Sorting, reversing strings, and factorial calculations are unrelated to pattern searching.
  3. Final Answer:

    To find occurrences of a pattern string within a text efficiently -> Option A
  4. Quick Check:

    KMP = Pattern search [OK]
Hint: KMP is about searching patterns fast in text [OK]
Common Mistakes:
  • Confusing KMP with sorting algorithms
  • Thinking KMP reverses strings
  • Assuming KMP calculates mathematical functions
2. Which of the following is the correct way to declare the prefix table (lps array) in Rust for KMP algorithm?
easy
A. let mut lps = Vec::new();
B. let lps = [0; pattern.len()];
C. let lps: Vec<i32> = Vec::with_capacity(pattern.len());
D. let mut lps: Vec<usize> = vec![0; pattern.len()];

Solution

  1. Step 1: Understand prefix table initialization

    The prefix table (lps) needs to be mutable and initialized with zeros for the length of the pattern.
  2. Step 2: Check each option

    let mut lps: Vec<usize> = vec![0; pattern.len()]; correctly creates a mutable vector of usize with zeros. let lps = [0; pattern.len()]; creates an array but Rust arrays need constant size. let mut lps = Vec::new(); creates an empty vector without size. let lps: Vec<i32> = Vec::with_capacity(pattern.len()); creates a vector with capacity but no elements initialized.
  3. Final Answer:

    let mut lps: Vec<usize> = vec![0; pattern.len()]; -> Option D
  4. Quick Check:

    Mutable zero-filled vector = let mut lps: Vec<usize> = vec![0; pattern.len()]; [OK]
Hint: Use mutable vector with zeros sized to pattern length [OK]
Common Mistakes:
  • Using fixed-size arrays without constant size
  • Not initializing vector elements
  • Using wrong data types for lps
3. What will be the output of the following Rust code snippet using KMP to find pattern "ABABC" in text "ABABABCABABC"?
fn main() {
    let text = "ABABABCABABC";
    let pattern = "ABABC";
    let positions = kmp_search(text, pattern);
    println!("{:?}", positions);
}

fn kmp_search(text: &str, pattern: &str) -> Vec<usize> {
    let lps = compute_lps(pattern);
    let mut result = Vec::new();
    let mut i = 0;
    let mut j = 0;
    while i < text.len() {
        if text.as_bytes()[i] == pattern.as_bytes()[j] {
            i += 1;
            j += 1;
            if j == pattern.len() {
                result.push(i - j);
                j = lps[j - 1];
            }
        } else if j > 0 {
            j = lps[j - 1];
        } else {
            i += 1;
        }
    }
    result
}

fn compute_lps(pattern: &str) -> Vec<usize> {
    let mut lps = vec![0; pattern.len()];
    let mut length = 0;
    let mut i = 1;
    while i < pattern.len() {
        if pattern.as_bytes()[i] == pattern.as_bytes()[length] {
            length += 1;
            lps[i] = length;
            i += 1;
        } else if length > 0 {
            length = lps[length - 1];
        } else {
            lps[i] = 0;
            i += 1;
        }
    }
    lps
}
medium
A. [0, 5, 7]
B. [2, 7]
C. [2, 7, 9]
D. [0, 5]

Solution

  1. Step 1: Understand KMP search output

    The function returns starting indices where the pattern "ABABC" appears in the text "ABABABCABABC".
  2. Step 2: Trace pattern matches

    Pattern matches at index 2 ("ABABC" in "ABABABC") and index 7 ("ABABC" in "ABABC"), so output is [2, 7].
  3. Final Answer:

    [2, 7] -> Option B
  4. Quick Check:

    Pattern found at indices 2 and 7 [OK]
Hint: Look for pattern start indices in text [OK]
Common Mistakes:
  • Counting overlapping matches incorrectly
  • Confusing indices starting at 0 or 1
  • Misreading pattern occurrences
4. Identify the bug in this KMP prefix table computation code snippet in Rust:
fn compute_lps(pattern: &str) -> Vec<usize> {
    let mut lps = vec![0; pattern.len()];
    let mut length = 0;
    let mut i = 0;
    while i < pattern.len() {
        if pattern.as_bytes()[i] == pattern.as_bytes()[length] {
            length += 1;
            lps[i] = length;
            i += 1;
        } else if length > 0 {
            length = lps[length - 1];
        } else {
            lps[i] = 0;
            i += 1;
        }
    }
    lps
}
medium
A. The loop should start with i = 1, not 0
B. The length variable should be initialized to 1
C. The lps vector should be of type Vec<i32>
D. The function should return a single usize, not a vector

Solution

  1. Step 1: Analyze loop initialization

    The prefix table computation starts comparing from the second character, so i must start at 1, not 0.
  2. Step 2: Check other variables and return type

    Length starts at 0 correctly, lps vector type usize is correct, and returning a vector is expected.
  3. Final Answer:

    The loop should start with i = 1, not 0 -> Option A
  4. Quick Check:

    Start i at 1 for prefix table [OK]
Hint: Prefix table loop starts at second character (i=1) [OK]
Common Mistakes:
  • Starting loop at 0 causing incorrect prefix values
  • Changing length initialization wrongly
  • Wrong return type assumptions
5. Given a text of length 1000 and a pattern of length 10, what is the worst-case time complexity of the KMP algorithm and why?
hard
A. O(n log m) because it uses binary search on the pattern
B. O(n * m) because it checks every character of text with every character of pattern
C. O(n + m) because KMP avoids rechecking characters using the prefix table
D. O(m^2) because prefix table computation is quadratic

Solution

  1. Step 1: Understand KMP time complexity

    KMP processes the text and pattern in linear time by using the prefix table to skip unnecessary comparisons.
  2. Step 2: Compare with other complexities

    Naive search is O(n*m), binary search is unrelated, and prefix table computation is O(m), not quadratic.
  3. Final Answer:

    O(n + m) because KMP avoids rechecking characters using the prefix table -> Option C
  4. Quick Check:

    KMP = linear time O(n + m) [OK]
Hint: KMP runs in linear time using prefix table [OK]
Common Mistakes:
  • Assuming nested loops cause O(n*m)
  • Confusing prefix table with binary search
  • Thinking prefix table is quadratic
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard