diff --git a/src/algorithms/searching/RabinKarp.js b/src/algorithms/searching/RabinKarp.js
new file mode 100644
index 0000000..2b7af7b
--- /dev/null
+++ b/src/algorithms/searching/RabinKarp.js
@@ -0,0 +1,147 @@
+export function getRabinKarpSteps(text, pattern) {
+ const steps = [];
+ const n = text.length;
+ const m = pattern.length;
+
+ if (m === 0 || n < m) {
+ return { steps, found: false };
+ }
+
+ const d = 256;
+ const q = 101;
+ let h = 1;
+ let pHash = 0;
+ let tHash = 0;
+ let found = false;
+
+ for (let i = 0; i < m - 1; i++) {
+ h = (h * d) % q;
+ }
+
+ steps.push({
+ windowStart: 0,
+ compareIndex: -1,
+ status: 'calculating',
+ pHash: 0,
+ tHash: 0,
+ message: `Starting. Pattern length=${m}, Text length=${n}. Prime q=${q}. Multiplier h=${h}.`
+ });
+
+ for (let i = 0; i < m; i++) {
+ pHash = (d * pHash + pattern.charCodeAt(i)) % q;
+ tHash = (d * tHash + text.charCodeAt(i)) % q;
+
+ steps.push({
+ windowStart: 0,
+ compareIndex: i,
+ status: 'calculating',
+ pHash,
+ tHash,
+ message: `Calculating initial hashes. Index ${i}. Pattern Hash = ${pHash}, Window Hash = ${tHash}.`
+ });
+ }
+
+ for (let i = 0; i <= n - m; i++) {
+ steps.push({
+ windowStart: i,
+ compareIndex: -1,
+ status: 'sliding',
+ pHash,
+ tHash,
+ message: `Sliding window to index ${i}. Pattern Hash = ${pHash}, Window Hash = ${tHash}.`
+ });
+
+ if (pHash === tHash) {
+ steps.push({
+ windowStart: i,
+ compareIndex: 0,
+ status: 'hash_match',
+ pHash,
+ tHash,
+ message: `Hash Match Found! Verifying character by character...`
+ });
+
+ let j = 0;
+ for (j = 0; j < m; j++) {
+ steps.push({
+ windowStart: i,
+ compareIndex: i + j,
+ patternCompareIndex: j,
+ status: 'verifying',
+ pHash,
+ tHash,
+ message: `Verifying: text[${i + j}] ('${text[i + j]}') === pattern[${j}] ('${pattern[j]}')?`
+ });
+
+ if (text[i + j] !== pattern[j]) {
+ steps.push({
+ windowStart: i,
+ compareIndex: i + j,
+ patternCompareIndex: j,
+ status: 'spurious_hit',
+ pHash,
+ tHash,
+ message: `Spurious Hit! Mismatch found. Resuming search.`
+ });
+ break;
+ }
+ }
+
+ if (j === m) {
+ found = true;
+ steps.push({
+ windowStart: i,
+ compareIndex: -1,
+ status: 'found',
+ pHash,
+ tHash,
+ message: `Pattern Found at index ${i}!`
+ });
+ }
+ }
+
+ if (i < n - m) {
+ const oldHash = tHash;
+ const charToRemove = text.charCodeAt(i);
+ const charToAdd = text.charCodeAt(i + m);
+
+ tHash = (d * (tHash - charToRemove * h) + charToAdd) % q;
+ if (tHash < 0) {
+ tHash += q;
+ }
+
+ steps.push({
+ windowStart: i,
+ compareIndex: -1,
+ status: 'rolling',
+ removeIndex: i,
+ addIndex: i + m,
+ pHash,
+ tHash: oldHash,
+ message: `Rolling hash... Removing '${text[i]}' (val ${charToRemove}).`
+ });
+
+ steps.push({
+ windowStart: i + 1,
+ compareIndex: -1,
+ status: 'rolling',
+ removeIndex: i,
+ addIndex: i + m,
+ pHash,
+ tHash,
+ message: `Rolling hash... Adding '${text[i + m]}' (val ${charToAdd}). New Window Hash = ${tHash}.`
+ });
+ }
+ }
+
+ steps.push({
+ windowStart: -1,
+ compareIndex: -1,
+ status: found ? 'finished_found' : 'finished_not_found',
+ pHash,
+ tHash,
+ message: found ? "Search complete. Pattern was found." : "Search complete. Pattern was not found."
+ });
+
+ return { steps, found };
+}
\ No newline at end of file
diff --git a/src/components/searching/RabinKarp.jsx b/src/components/searching/RabinKarp.jsx
new file mode 100644
index 0000000..ba05237
--- /dev/null
+++ b/src/components/searching/RabinKarp.jsx
@@ -0,0 +1,319 @@
+import React, { useState, useMemo, useEffect, useRef } from "react";
+// Assumes 'algorithms' is at 'src/algorithms/'
+import { getRabinKarpSteps } from "../../algorithms/searching/RabinKarp";
+
+const StringDisplay = ({ text, highlights = {} }) => {
+ return (
+
+ {text.split("").map((char, index) => {
+ let
+ bgColor = "bg-gray-700",
+ textColor = "text-gray-200",
+ borderColor = "border-gray-600";
+
+ if (highlights[index]) {
+ const type = highlights[index];
+ if (type === "match") {
+ bgColor = "bg-green-600";
+ textColor = "text-white";
+ borderColor = "border-green-400";
+ } else if (type === "spurious") {
+ bgColor = "bg-yellow-600";
+ textColor = "text-white";
+ borderColor = "border-yellow-400";
+ } else if (type === "compare") {
+ bgColor = "bg-blue-600";
+ textColor = "text-white";
+ borderColor = "border-blue-400";
+ }
+ }
+
+ return (
+
+ {char}
+
+ );
+ })}
+
+ );
+};
+
+const PatternDisplay = ({ pattern, highlights = {} }) => {
+ return (
+
+ {pattern.split("").map((char, index) => {
+ let
+ bgColor = "bg-gray-700",
+ textColor = "text-gray-200",
+ borderColor = "border-gray-600";
+
+ if (highlights[index]) {
+ const type = highlights[index];
+ if (type === "match") {
+ bgColor = "bg-green-600";
+ textColor = "text-white";
+ borderColor = "border-green-400";
+ } else if (type === "mismatch") {
+ bgColor = "bg-red-600";
+ textColor = "text-white";
+ borderColor = "border-red-400";
+ }
+ }
+
+ return (
+
+ {char}
+
+ );
+ })}
+
+ );
+};
+
+
+// Playback speeds in milliseconds
+const SPEED_OPTIONS = {
+ "Slow": 1500,
+ "Medium": 500,
+ "Fast": 200,
+};
+
+export default function RabinKarp() {
+ const [text, setText] = useState("AABAACAADAABAABA");
+ const [pattern, setPattern] = useState("AABA");
+ const [steps, setSteps] = useState([]);
+ const [currentStep, setCurrentStep] = useState(0);
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [speed, setSpeed] = useState(SPEED_OPTIONS["Medium"]);
+ const timerRef = useRef(null);
+
+ // --- Core Logic ---
+
+ const handleCompute = () => {
+ if (!text || !pattern) {
+ alert("Text and Pattern cannot be empty.");
+ return;
+ }
+ if (pattern.length > text.length) {
+ alert("Pattern cannot be longer than the text.");
+ return;
+ }
+
+ setIsPlaying(false);
+ const { steps: newSteps } = getRabinKarpSteps(text, pattern);
+ setSteps(newSteps);
+ setCurrentStep(0);
+ };
+
+ useEffect(() => {
+ handleCompute();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ useEffect(() => {
+ if (isPlaying && currentStep < steps.length - 1) {
+ timerRef.current = setInterval(() => {
+ setCurrentStep((prevStep) => prevStep + 1);
+ }, speed);
+ } else if (currentStep === steps.length - 1) {
+ setIsPlaying(false);
+ }
+
+ return () => {
+ clearInterval(timerRef.current);
+ };
+ }, [isPlaying, currentStep, steps.length, speed]);
+
+ // --- Handlers ---
+
+ const togglePlay = () => {
+ if (currentStep === steps.length - 1) {
+ setCurrentStep(0);
+ setIsPlaying(true);
+ } else {
+ setIsPlaying(!isPlaying);
+ }
+ };
+
+ const handleNext = () => {
+ setIsPlaying(false);
+ if (currentStep < steps.length - 1) setCurrentStep(currentStep + 1);
+ };
+
+ const handlePrev = () => {
+ setIsPlaying(false);
+ if (currentStep > 0) setCurrentStep(currentStep - 1);
+ };
+
+ const currentState = useMemo(() => steps[currentStep] || {}, [steps, currentStep]);
+ const { status, textHighlights, patternHighlights, textHash, patternHash, message } = currentState;
+
+ const isFinalStep = steps.length > 0 && currentStep === steps.length - 1;
+
+ return (
+
+
+
+ Rabin-Karp String Search
+
+
+ {/* --- What is Rabin-Karp? --- */}
+
+
+ ❓ What is Rabin-Karp?
+
+
+
+ Rabin-Karp is a string matching algorithm that uses a **hashing function** to find a pattern in text.
+
+
+ - It calculates a hash for the pattern and for each "window" of the text.
+ - It uses a **"rolling hash"** to quickly calculate the next window's hash from the previous one.
+ - If hashes match, it performs a character-by-character check to avoid **"spurious hits"** (where different strings have the same hash).
+
+
+
+ {/* --- End of What is Rabin-Karp? --- */}
+
+
+
+
+
+ setText(e.target.value)}
+ />
+
+
+
+
+ setPattern(e.target.value)}
+ />
+
+
+
+
+
+ {steps.length > 0 ? (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Step {currentStep + 1} / {steps.length}
+
+
+
+
+
+
+
Current Action
+
+ {message || 'Starting...'}
+
+
+
+
+
+
Window Hash
+
+ {textHash}
+
+
+
+
Pattern Hash
+
+ {patternHash}
+
+
+
+
+
+
Text
+
+
+
+
+
+ {isFinalStep && (
+
+
+ {status === 'finished_found' ? '🎉 Pattern Found! 🎉' : 'Search Complete. Pattern Not Found.'}
+
+
+ )}
+
+
+ >
+ ) : (
+
+
Enter text and a pattern, then click "Visualize" to begin.
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/searching/RabinKarp.jsx b/src/pages/searching/RabinKarp.jsx
new file mode 100644
index 0000000..6726b5f
--- /dev/null
+++ b/src/pages/searching/RabinKarp.jsx
@@ -0,0 +1,6 @@
+import RabinKarpVisualizer from "../../components/searching/RabinKarp";
+import React from "react";
+
+export default function RabinKarpPage() {
+ return ;
+}
\ No newline at end of file
diff --git a/src/pages/searching/SearchingPage.jsx b/src/pages/searching/SearchingPage.jsx
index 22b9346..79756f5 100644
--- a/src/pages/searching/SearchingPage.jsx
+++ b/src/pages/searching/SearchingPage.jsx
@@ -1,9 +1,9 @@
-// src/pages/SortingPage.jsx
import React, { useState } from "react";
import LinearSearch from "./linearSearch";
import BinarySearch from "./BinarySearch";
+import RabinKarp from "./RabinKarp";
-export default function SortingPage() {
+export default function SearchingPage() {
const [selectedAlgo, setSelectedAlgo] = useState("");
const renderAlgorithm = () => {
@@ -12,7 +12,9 @@ export default function SortingPage() {
return ;
case "BinarySearch":
return ;
-
+ case "RabinKarp":
+ return ;
+
default:
return (
@@ -24,7 +26,6 @@ export default function SortingPage() {
return (
- {/* Left Sidebar */}
Searching Panel
@@ -37,8 +38,9 @@ export default function SortingPage() {
className="w-full p-2 rounded bg-[#1e293b] text-white border border-gray-600 cursor-pointer"
>
-
-
+
+
+
- {/* Right Visualization Area */}
{renderAlgorithm()}
);
-}
+}
\ No newline at end of file