compute_failure_function <- function(pattern) {
m <- nchar(pattern)
failure <- rep(0, m)
if (m == 0) return(integer(0))
if (m == 1) return(0)
j <- 0
for (i in 2:m) {
curr_char <- substr(pattern, i, i)
while (j > 0 && substr(pattern, j + 1, j + 1) != curr_char) {
j <- failure[j]
}
if (substr(pattern, j + 1, j + 1) == curr_char) {
j <- j + 1
}
failure[i] <- j
}
return(failure)
}
kmp_search <- function(text, pattern) {
n <- nchar(text)
m <- nchar(pattern)
if (m == 0) return(integer(0))
if (n == 0 || m > n) return(integer(0))
failure <- compute_failure_function(pattern)
matches <- c()
j <- 0
for (i in 1:n) {
curr_char <- substr(text, i, i)
while (j > 0 && substr(pattern, j + 1, j + 1) != curr_char) {
j <- failure[j]
}
if (substr(pattern, j + 1, j + 1) == curr_char) {
j <- j + 1
}
if (j == m) {
matches <- c(matches, i - m + 1)
j <- failure[j]
}
}
return(matches)
}
kmp_search_first <- function(text, pattern) {
n <- nchar(text)
m <- nchar(pattern)
if (m == 0) return(-1)
if (n == 0 || m > n) return(-1)
failure <- compute_failure_function(pattern)
j <- 0
for (i in 1:n) {
curr_char <- substr(text, i, i)
while (j > 0 && substr(pattern, j + 1, j + 1) != curr_char) {
j <- failure[j]
}
if (substr(pattern, j + 1, j + 1) == curr_char) {
j <- j + 1
}
if (j == m) {
return(i - m + 1)
}
}
return(-1)
}
kmp_count <- function(text, pattern) {
n <- nchar(text)
m <- nchar(pattern)
if (m == 0) return(0)
if (n == 0 || m > n) return(0)
failure <- compute_failure_function(pattern)
count <- 0
j <- 0
for (i in 1:n) {
curr_char <- substr(text, i, i)
while (j > 0 && substr(pattern, j + 1, j + 1) != curr_char) {
j <- failure[j]
}
if (substr(pattern, j + 1, j + 1) == curr_char) {
j <- j + 1
}
if (j == m) {
count <- count + 1
j <- failure[j]
}
}
return(count)
}
naive_search <- function(text, pattern) {
n <- nchar(text)
m <- nchar(pattern)
matches <- c()
if (m == 0 || n == 0 || m > n) return(matches)
for (i in 1:(n - m + 1)) {
if (substr(text, i, i + m - 1) == pattern) {
matches <- c(matches, i)
}
}
return(matches)
}
visualize_failure_function <- function(pattern) {
failure <- compute_failure_function(pattern)
m <- nchar(pattern)
cat("Pattern: ")
for (i in 1:m) {
cat(sprintf("%2s ", substr(pattern, i, i)))
}
cat("\n")
cat("Index: ")
for (i in 1:m) {
cat(sprintf("%2d ", i))
}
cat("\n")
cat("Failure: ")
for (i in 1:m) {
cat(sprintf("%2d ", failure[i]))
}
cat("\n\n")
}
cat("=== Knuth-Morris-Pratt (KMP) String Matching Algorithm ===\n\n")
cat("1. Basic Pattern Matching\n")
text1 <- "ABABDABACDABABCABCABCABCABC"
pattern1 <- "ABC"
cat("Text: ", text1, "\n")
cat("Pattern: ", pattern1, "\n")
matches1 <- kmp_search(text1, pattern1)
cat("KMP matches at positions:", paste(matches1, collapse = ", "), "\n")
naive_matches1 <- naive_search(text1, pattern1)
cat("Naive matches at positions:", paste(naive_matches1, collapse = ", "), "\n")
cat("Results match:", identical(matches1, naive_matches1), "\n\n")
cat("2. Pattern with Repeating Characters\n")
text2 <- "AABAACAADAABAABA"
pattern2 <- "AABA"
cat("Text: ", text2, "\n")
cat("Pattern: ", pattern2, "\n")
visualize_failure_function(pattern2)
matches2 <- kmp_search(text2, pattern2)
cat("Matches at positions:", paste(matches2, collapse = ", "), "\n")
for (pos in matches2) {
match_str <- substr(text2, pos, pos + nchar(pattern2) - 1)
cat("Position", pos, ":", match_str, "\n")
}
cat("\n")
cat("3. Edge Cases\n")
cat("Empty pattern:", length(kmp_search("hello", "")), "matches\n")
cat("Empty text:", length(kmp_search("", "hello")), "matches\n")
cat("Pattern longer than text:", length(kmp_search("hi", "hello")), "matches\n")
cat("Single character pattern:", paste(kmp_search("abcabc", "a"), collapse = ", "), "\n")
cat("Pattern not in text:", paste(kmp_search("hello world", "xyz"), collapse = ", "), "\n")
cat("Identical strings:", paste(kmp_search("hello", "hello"), collapse = ", "), "\n")
cat("Pattern at start:", paste(kmp_search("hello world", "hello"), collapse = ", "), "\n")
cat("Pattern at end:", paste(kmp_search("world hello", "hello"), collapse = ", "), "\n\n")
cat("4. Performance Comparison\n")
repeated_text <- paste(rep("AAAAB", 200), collapse = "")
repeated_pattern <- "AAAAB"
cat("Text length:", nchar(repeated_text), "\n")
cat("Pattern:", repeated_pattern, "\n")
start_time <- Sys.time()
kmp_result <- kmp_search(repeated_text, repeated_pattern)
kmp_time <- as.numeric(Sys.time() - start_time, units = "secs")
start_time <- Sys.time()
naive_result <- naive_search(repeated_text, repeated_pattern)
naive_time <- as.numeric(Sys.time() - start_time, units = "secs")
cat("KMP found", length(kmp_result), "matches in", sprintf("%.6f", kmp_time), "seconds\n")
cat("Naive found", length(naive_result), "matches in", sprintf("%.6f", naive_time), "seconds\n")
if (naive_time > 0 && kmp_time > 0) {
speedup <- naive_time / kmp_time
cat("KMP speedup:", sprintf("%.2f", speedup), "x faster\n")
}
cat("\n")
cat("5. DNA Sequence Matching Example\n")
dna_sequence <- "ATCGATCGATCGAATCGATCGATCGAATCGATCG"
dna_pattern <- "ATCG"
cat("DNA Sequence:", dna_sequence, "\n")
cat("Pattern: ", dna_pattern, "\n")
dna_matches <- kmp_search(dna_sequence, dna_pattern)
cat("Pattern occurs at positions:", paste(dna_matches, collapse = ", "), "\n")
cat("Total occurrences:", length(dna_matches), "\n\n")
cat("6. Failure Function Examples\n")
patterns <- c("ABCABCAB", "AAAA", "ABCDE", "ABABABAB")
for (pattern in patterns) {
cat("Pattern:", pattern, "\n")
visualize_failure_function(pattern)
}
cat("7. Case Sensitivity\n")
text_case <- "Hello World Hello"
pattern_case <- "hello"
matches_case <- kmp_search(text_case, pattern_case)
cat("Text:", text_case, "\n")
cat("Pattern:", pattern_case, "\n")
cat("Matches (case-sensitive):", paste(matches_case, collapse = ", "), "\n")
matches_insensitive <- kmp_search(tolower(text_case), tolower(pattern_case))
cat("Matches (case-insensitive):", paste(matches_insensitive, collapse = ", "), "\n")