Open In App

How to make KMP Algorithm visualizer using HTML,CSS & JavaScript ?

Last Updated : 23 Feb, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

The KMP matching algorithm uses degenerating property (pattern having the same sub-patterns appearing more than once in the pattern) of the pattern and improves the worst-case complexity to O(n). The basic idea behind KMP’s algorithm is: whenever we detect a mismatch (after some matches), we already know some of the characters in the text of the next window. We take advantage of this information to avoid matching the characters that we know will anyway match.

Example:

Matching Overview
txt = "AAAAABAAABA" 
pat = "AAAA"
We compare first window of txt with pat
txt = "AAAAABAAABA" 
pat = "AAAA"  [Initial position]
We find a match. This is same as Naive String Matching.
In the next step, we compare next window of txt with pat.
txt = "AAAAABAAABA" 
pat =  "AAAA" [Pattern shifted one position]

This is where KMP does optimization over Naive. In this second window, we only compare the fourth A of the pattern
with the fourth character of the current window of text to decide whether the current window matches or not. Since we know 
the first three characters will anyway match, we skipped matching the first three characters.

In this article, we will see How to make a KMP Algorithm visualizer using HTML, CSS & JavaScript.

Approach: We will use the KMP Algorithm but we will be taking input of text and pattern and then we will be computing an LPS array then we will compare text and pattern and we will highlight the ways in which we are conducting the algorithm which visualize it to the user

Below is the step-by-step implementation:

Step 1: We need to now understand the HTML part of how we made the containers in HTML in order to divide we made a container that holds all of our elements like a displayer which holds elements that are to be displayed on the screen and then we have our text which holds our text followed by pattern and pattern text a message div then we have input tags for pattern and text, in the end, we have start button which at the end begins the algorithm visualization.

index.html

HTML




<!DOCTYPE html>
<html lang="en">
  
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,     
        initial-scale=1.0">
    <link href=
          rel="stylesheet" />
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
    <title>Document</title>
</head>
  
<body>
    <h1>
        <span class="1">K</span>ruth <span class="2">
              M
         </span>orris
        <span class="3">P</span>rat
        <span>Algorithm</span>
    </h1>
    <div id="container">
        <div id="displayer">
            <div id="text"></div>
            <div id="pattern"></div>
            <div id="pattern_text"></div>
            <div id="message"></div>
        </div>
        <input id="input" type="text" 
            placeholder="Enter the Text">
        <input id="input1" type="text" 
            placeholder="Enter the Pattern">
        <div id="start">Begin</div>
    </div>
</body>
  
</html>


Step 2: We will have black background so we gave the text color as white to every element so that it shows and then for the span we have to make it glow so we give a text-shadow we set our HTML background color and font family now for the body we make sure everything remains center and set flex-direction as a center and for container also we give it some position same goes for the container we give it a letter spacing so that it looks neat we make a tile and make sure every element displayed is with tile for symmetric we set on hover and change color to cyan and then we style every element for its positions

style.css

CSS




* {
    color: white;
    font-family: "Open sans", sans-serif;
}
  
html {
    background-color: black;
}
  
body {
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100vmin;
}
  
h1 span {
    font-size: 6vmin;
    font-weight: normal;
    text-shadow: 0 0 20px cyan,
        0 0 40px cyan,
        0 0 80px cyan;
}
  
#container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 80%;
    width: 80%;
}
  
#displayer {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    height: 90%;
}
  
#text,
#pattern,
#message {
    width: 100%;
    height: 7vmin;
    margin: 3vmin;
    font-size: 5vmin;
    display: flex;
    align-items: center;
    justify-content: center;
}
  
#message {
    color: cyan;
    font-size: 2vmin;
}
  
#pattern_text,
#array_text {
    width: 100%;
    height: 5vmin;
    margin: 3vmin;
    font-size: 5vmin;
    display: flex;
    align-items: center;
    justify-content: center;
    color: g;
}
  
#pattern_text#array_text {
    width: 100%;
    height: 5vmin;
    margin: 3vmin;
    font-size: 5vmin;
    display: flex;
    align-items: center;
    justify-content: center;
    color: g;
}
  
.tile {
    width: 6vmin;
    height: 6vmin;
    margin: 10px;
    text-align: center;
    height: fit-content;
    border: 1px solid black;
}
  
#start {
    align-self: center;
    background-color: black;
    font-size: 3vmin;
    box-sizing: border-box;
    padding: 1vmin;
    color: white;
    cursor: pointer;
    border: none;
    margin-top: 2vmin;
    transition: 0.5s ease-in-out;
    font-weight: bold;
    letter-spacing: 4px;
}
  
#start:hover {
    transform: scale(1.5);
    text-shadow: 0 0 10px cyan,
        0 0 20px cyan,
        0 0 40px cyan;
}
  
#input,
#input1 {
    width: 40%;
    height: 7%;
    border: none;
    border-radius: 7px;
    background-color: #333;
    color: white;
    margin: 1vmin;
    font-size: 2vmin;
    transition: 0.5s ease-in-out;
}
  
#input::placeholder,
#input1::placeholder {
    font-size: 3vmin;
    text-align: center;
    color: white;
}
  
h1 {
    margin-top: 0;
    text-align: center;
    padding: 1vmin;
    margin-bottom: 1vmin;
    width: 100%;
    font-size: 5vmin;
    font-weight: normal;
    letter-spacing: 2px;
    border-bottom: 1px solid white;
}


Step3: like in this example we first computed the below LPS array and then when the 1st element repeats we shift the element counter to the next element and then when the p again repeats we make the index of lps array where the element repeats as 1 then when so then we iterate over the text and we compare and look for the first element we first found it on 3rd index of the text so we compare it with the next element of pattern and 4th index of text it is equal we now compare the next element of text to next element of pattern it is not equal now we send the counter on the pattern to the element to index of element which is not equal i.e 2nd index of pattern and 2nd index of lps array is 1 so we send it to 1st index then we compare next index of text i.e 5 and 1st index of pattern it is not equal we send the counter of pattern on index 1 of lps array i.e 0 now we again compare 6th index of text to 0th index of the pattern not equal now we again compare and then we found the pattern on 7th index.

script.js

Javascript




function id(id) {
    return document.getElementById(id);
}
var count = 0;
var pattern, text, Psize, Tsize;
var idcountrater = 0;
var conti = 0;
const LpsComputation = async (pattern, Psize, lps, resolve) => {
    let len = 0;
    let i = 1;
    let j = 0;
    id("message").innerText = " ";
    lps[0] = 0;
    for (let i = 0; i < Psize; i++) {
  
        var tile = document.createElement('span')
        tile.classList.add("tile");
        tile.innerText = "0"
        tile.id = idcount;
        id("pattern_text").appendChild(tile);
        idcount++;
    }
  
    id(idcount - Psize + idcountrater).
        style.borderColor = "orange";
    id(idcount - Psize + idcountrater).innerText = "0";
    id(idcount - Psize + idcountrater).
        style.color = "orange";
    await new Promise((resolve) =>
        setTimeout(() => {
            resolve();
        }, 3000)
    )
    id(idcount - Psize + idcountrater).
        style.borderColor = "black";
    id(idcount - Psize + idcountrater).
        style.color = "white"
    // id(`${idcount}`)
    await new Promise((resolve) =>
        setTimeout(() => {
            resolve();
        }, 2000)
    )
    idcountrater++;
    while (i < Psize) {
        if (pattern[i] == pattern[len]) {
            id((idcount - Psize) + len).
                style.borderColor = "cyan";
            id((idcount - Psize) + len).
                style.color = "cyan";
            id((idcount - Psize) + i).
                style.borderColor = "cyan";
            id((idcount - Psize) + i).
                style.color = "cyan";
            await new Promise((resolve) =>
                setTimeout(() => {
                    resolve();
                }, 2000)
            )
            id((idcount - Psize) + len + 1).
                style.borderColor = "black";
            id((idcount - Psize) + len + 1).
                style.color = "white";
            id((idcount - Psize) + len).
                style.borderColor = "black";
            id((idcount - Psize) + len).
                style.color = "white";
            len++;
            lps[i] = len;
  
            id((idcount - Psize) + i).
                style.borderColor = "cyan";
            id((idcount - Psize) + i).
                innerText = len;
            id((idcount - Psize) + i).
                style.color = "cyan";
            await new Promise((resolve) =>
                setTimeout(() => {
                    resolve();
                }, 2000)
            )
            id((idcount - Psize) + i).
                style.borderColor = "black";
            id((idcount - Psize) + i).
                style.color = "white";
            i++;
        } else {
            if (len != 0) {
  
                id((idcount - Psize) + len).
                    style.borderColor = "red";
                id((idcount - Psize) + len).
                    style.color = "red";
                id("message").innerText =
                    `since the next element is not equal
                    we need to compare from where 
                    character is repeating in pattern`
                await new Promise((resolve) =>
                    setTimeout(() => {
                        resolve();
                    }, 4000)
                )
                id((idcount - Psize) + len).style.borderColor
                    = "black";
                id((idcount - Psize) + len).style.color
                    = "white";
                len = lps[len - 1];
                id("message").innerText = `we sent 
                    it to ${len} index`;
                id((idcount - Psize) + len).style.borderColor
                    = "green";
                id((idcount - Psize) + len).style.color
                    = "green";
                await new Promise((resolve) =>
                    setTimeout(() => {
                        id("message").innerText = ``;
                        resolve();
                    }, 3000)
                )
                id((idcount - Psize) + len).style.borderColor
                    = "black";
                id((idcount - Psize) + len).style.color
                    = "white";
            } else {
                lps[i] = 0;
                id((idcount - Psize) + i).style.borderColor
                    = "orange";
                id((idcount - Psize) + i).style.color
                    = "orange";
                await new Promise((resolve) =>
                    setTimeout(() => {
                        resolve();
                    }, 3000)
                )
                id((idcount - Psize) + i).style.borderColor
                    = "black";
                id((idcount - Psize) + i).style.color
                    = "white";
                i++;
            }
        }
    } conti = 1;
  
    resolve();
}
const kmp = async (pattern, text, Psize, Tsize) => {
    var lps = [];
    id("message").innerText = `Computing the LPS array first`;
    await new Promise((resolve) =>
        setTimeout(() => {
            resolve();
        }, 4000)
    )
  
    await new Promise((resolve) => {
        LpsComputation(pattern, Psize, lps, resolve);
    })
    id("message").innerText =
        `Now after Computation beginning with 
         the comparison process`;
    await new Promise((resolve) =>
        setTimeout(() => {
            id("message").innerText = " ";
            resolve();
        }, 3000)
    )
    console.log("inside kmp");
    var i = 0;
    var j = 0;
    console.log(`pattern is ${pattern} text is ${text} Tsize 
                 is ${Tsize} Psize ${Psize}`);
    while (i < Tsize) {
        console.log("inside for loop");
        if (pattern[j] == text[i]) {
            console.log("if");
            id(idcount - (Tsize + Psize) - Psize + i).
                style.borderColor = "orange";
            id(idcount - (Tsize + Psize) - Psize + i).
                style.color = "orange";
            id((idcount - Psize) - Psize + j)
                .style.borderColor = "orange";
            id((idcount - Psize) - Psize + j).style.color
                = "orange";
            await new Promise((resolve) =>
                setTimeout(() => {
                    resolve();
                }, 3000)
            )
            id(idcount - (Tsize + Psize) - Psize + i)
                .style.borderColor = "cyan";
            id(idcount - (Tsize + Psize) - Psize + i)
                .style.color = "cyan";
            id((idcount - Psize) - Psize + j)
                .style.borderColor = "cyan";
            id((idcount - Psize) - Psize + j).style.color
                = "cyan";
            await new Promise((resolve) =>
                setTimeout(() => {
                    resolve();
                }, 3000)
            )
            id(idcount - (Tsize + Psize) - Psize + i)
                .style.borderColor = "black";
            id(idcount - (Tsize + Psize) - Psize + i)
                .style.color = "white";
            id((idcount - Psize) - Psize + j)
                .style.borderColor = "black";
            id((idcount - Psize) - Psize + j)
                .style.color = "white";
            console.log(`i is ${i} j is ${j}`);
            i++; j++;
        }
        if (j == Psize) {
            console.log(`inside i is ${i} j is${j}`);
            let x = 0;
            while (x < j) {
                id(i - Psize).style.borderColor = "greenyellow";
                id(i - Psize).style.color = "greenyellow";
                id(i).style.borderColor = "greenyellow";
                id(i).style.color = "greenyellow";
                i++;
                x++;
            }
            id("message").innerText =
                "Pattern Found inside of the Text"
            return;
        } else if (i < Tsize && pattern[j] != text[i]) {
            console.log("inside else if");
            if (j != 0) {
                console.log("INSIDE PROBLEM");
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.borderColor
                    = "orange";
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.color
                    = "orange";
                id((idcount - Psize) - Psize + j)
                    .style.borderColor
                    = "orange";
                id((idcount - Psize) - Psize + j)
                    .style.color
                    = "orange";
                await new Promise((resolve) =>
                    setTimeout(() => {
                        resolve();
                    }, 3000)
                )
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.borderColor
                    = "black";
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.color
                    = "white";
                id((idcount - Psize) - Psize + j)
                    .style.borderColor
                    = "black";
                id((idcount - Psize) - Psize + j)
                    .style.color
                    = "white";
                j = lps[j - 1];
                console.log('logging in');
            } else {
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.borderColor
                    = "orange";
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.color
                    = "orange";
                await new Promise((resolve) =>
                    setTimeout(() => {
                        resolve();
                    }, 3000)
                )
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.borderColor
                    = "black";
                id(idcount - (Tsize + Psize) - Psize + i)
                    .style.color
                    = "white";
                i = i + 1;
            }
        }
    }
}
let idcount = 0;
window.onload = async () => {
    id("displayer").style.display = "none";
    id("start").addEventListener('click', () => {
        id("displayer").style.display = "flex";
        pattern = id("input1").value;
        Psize = pattern.length
        text = id("input").value;
        Tsize = text.length;
        id("input1").style.display = "none"
        id("input").style.display = "none"
        id("start").style.display = "none"
        for (let i = 0; i < Tsize; i++) {
            let tile = document.createElement('span');
            tile.id = idcount;
            tile.classList.add("tile");
            tile.innerText = text[i];
            id("text").appendChild(tile);
            idcount++;
        }
        let idcount1 = 0;
        for (let i = 0; i < Psize; i++) {
            let tile = document.createElement('span');
            tile.id = idcount;
            tile.classList.add("tile");
            tile.innerText = pattern[i];
            id("pattern").appendChild(tile);
            idcount++;
        }
        kmp(pattern, text, Psize, Tsize);
    })
}


Output:

KMP Algorithm



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads