--------------------------------------------------------------------------------------------------------------------------------
Given a text txt [0...n-1] and a pattern pat [0...m-1], prints all 
occurrences of pat [ ] in txt [ ] by using the Brute force string matching 
approach. You may assume that n > m. 

public class BruteForceStringMatching {

    // Function to perform brute force pattern matching
    public static void search(String txt, String pat) {
        int n = txt.length();
        int m = pat.length();

        System.out.println("Pattern found at positions:");

        for (int i = 0; i <= n - m; i++) {
            int j;

            // Check for pattern match at current position
            for (j = 0; j < m; j++) {
                if (txt.charAt(i + j) != pat.charAt(j)) {
                    break;
                }
            }

            // If full pattern matched
            if (j == m) {
                System.out.println("Pattern found at index " + i);
            }
        }
    }

    public static void main(String[] args) {
        // Example text and pattern
        String txt = "ABAAABCDBBABCDDEBCABC";
        String pat = "ABC";

        search(txt, pat);
    }
}



----------------------------------------------------------------------------------------------------------------------------------
Given a text txt [0...n-1] and a pattern pat [0...m-1], prints all 
occurrences of pat [ ]in txt [ ] by using the KMP approach. You may 
assume that n > m. 

public class KMPStringMatching {

    // Function to compute the LPS (Longest Prefix Suffix) array
    public static void computeLPSArray(String pat, int[] lps) {
        int m = pat.length();
        int len = 0; // length of the previous longest prefix suffix
        lps[0] = 0;  // lps[0] is always 0

        int i = 1;
        while (i < m) {
            if (pat.charAt(i) == pat.charAt(len)) {
                len++;
                lps[i] = len;
                i++;
            } else {
                if (len != 0) {
                    len = lps[len - 1]; // Try with previous longest prefix
                } else {
                    lps[i] = 0;
                    i++;
                }
            }
        }
    }

    // KMP pattern matching function
    public static void KMPSearch(String pat, String txt) {
        int m = pat.length();
        int n = txt.length();

        int[] lps = new int[m];
        computeLPSArray(pat, lps);

        int i = 0; // index for txt[]
        int j = 0; // index for pat[]

        System.out.println("Pattern found at positions:");

        while (i < n) {
            if (pat.charAt(j) == txt.charAt(i)) {
                i++;
                j++;
            }

            if (j == m) {
                System.out.println("Pattern found at index " + (i - j));
                j = lps[j - 1]; // Continue searching for next match
            } else if (i < n && pat.charAt(j) != txt.charAt(i)) {
                if (j != 0)
                    j = lps[j - 1];
                else
                    i++;
            }
        }
    }

    // Driver code
    public static void main(String[] args) {
        String txt = "ABABDABACDABABCABAB";
        String pat = "ABABCABAB";

        KMPSearch(pat, txt);
    }
}



✅ KMP Algorithm – High-Level Steps
Input:
Text string txt[0...n-1]

Pattern string pat[0...m-1]

Step 1: Preprocess the Pattern
Goal: Build the LPS (Longest Prefix Suffix) array for the pattern.

Initialize an array lps[] of size m.

Set lps[0] = 0.

For each position i from 1 to m-1:

Compare pat[i] with the character at current prefix length.

If they match, increase prefix length and store it in lps[i].

If they don't match and the prefix length is not zero, move back using lps[len-1].

Else, set lps[i] = 0.

Step 2: Search Pattern in Text
Goal: Use the LPS array to avoid redundant comparisons.

Initialize two pointers:

i for txt, starting at 0

j for pat, starting at 0

While i < n:

If txt[i] == pat[j], increment both i and j.

If j == m:

Pattern found at index i - j.

Use lps[j - 1] to update j and continue searching.

If mismatch and j > 0, update j = lps[j - 1].

Else, increment i.

Output:
Indices in txt[] where the pattern pat[] occurs.




----------------------------------------------------------------------------------------------------------------------------------
Given a text txt [0...n-1] and a pattern pat [0...m-1], prints all 
occurrences of pat [ ] in txt [ ] by using the Rabin Krap approach. You 
may assume that n > m. 

public class RabinKarp {

    // d is the number of characters in the input alphabet (e.g., 256 for extended ASCII)
    final static int d = 256;

    // A prime number for modulo operations
    final static int q = 101;

    public static void search(String pat, String txt) {
        int m = pat.length();
        int n = txt.length();
        int i, j;
        int p = 0; // hash value for pattern
        int t = 0; // hash value for text window
        int h = 1;

        // The value of h would be "pow(d, m-1)%q"
        for (i = 0; i < m - 1; i++)
            h = (h * d) % q;

        // Calculate the hash value of pattern and first window of text
        for (i = 0; i < m; i++) {
            p = (d * p + pat.charAt(i)) % q;
            t = (d * t + txt.charAt(i)) % q;
        }

        // Slide the pattern over text one by one
        for (i = 0; i <= n - m; i++) {

            // Check the hash values of current window of text and pattern
            if (p == t) {
                // If hash values match, check characters one by one
                for (j = 0; j < m; j++) {
                    if (txt.charAt(i + j) != pat.charAt(j))
                        break;
                }

                if (j == m) {
                    System.out.println("Pattern found at index " + i);
                }
            }

            // Calculate hash value for next window of text
            if (i < n - m) {
                t = (d * (t - txt.charAt(i) * h) + txt.charAt(i + m)) % q;

                // We might get negative value of t, converting it to positive
                if (t < 0)
                    t = (t + q);
            }
        }
    }

    public static void main(String[] args) {
        String txt = "GEEKS FOR GEEKS";
        String pat = "GEEK";
        search(pat, txt);
    }
}


--------------------------------------------------------------------------------------------------------------------------
Given a text txt [0...n-1] and a pattern pat [0...m-1], prints all 
occurrences of pat [ ] in txt [ ] by using the Naïve string matching 
approach. You may assume that n > m. 

public class NaiveStringMatching {

    public static void search(String txt, String pat) {
        int n = txt.length();
        int m = pat.length();

        System.out.println("Pattern found at positions:");

        for (int i = 0; i <= n - m; i++) {
            int j;

            // Check if the pattern matches at position i
            for (j = 0; j < m; j++) {
                if (txt.charAt(i + j) != pat.charAt(j)) {
                    break;
                }
            }

            // If j reached the end, the pattern matched
            if (j == m) {
                System.out.println("Pattern found at index " + i);
            }
        }
    }

    public static void main(String[] args) {
        String txt = "AABAACAADAABAABA";
        String pat = "AABA";

        search(txt, pat);
    }
}




--------------------------------------------------------------------------------------------------------------------------------
 Given a text txt [0...n-1] and a pattern pat [0...m-1], prints all 
occurrences of pat [ ] in txt [ ] by using the Boyer Moore algorithm. 
You may assume that n > m. 


public class BoyerMoore {

    static int NO_OF_CHARS = 256;

    // Preprocessing: Fill the bad character array
    static void badCharHeuristic(String str, int size, int badchar[]) {
        // Initialize all occurrences as -1
        for (int i = 0; i < NO_OF_CHARS; i++)
            badchar[i] = -1;

        // Fill the actual value of last occurrence of a character
        for (int i = 0; i < size; i++)
            badchar[(int) str.charAt(i)] = i;
    }

    // Search function for Boyer Moore algorithm
    static void search(String txt, String pat) {
        int m = pat.length();
        int n = txt.length();

        int[] badchar = new int[NO_OF_CHARS];

        // Fill the bad character array
        badCharHeuristic(pat, m, badchar);

        int s = 0; // s is the shift of the pattern with respect to text
        while (s <= (n - m)) {
            int j = m - 1;

            // Keep reducing j while characters match
            while (j >= 0 && pat.charAt(j) == txt.charAt(s + j))
                j--;

            // If the pattern is present at current shift, j becomes -1
            if (j < 0) {
                System.out.println("Pattern found at index " + s);

                // Shift pattern so next character in text aligns with last occurrence in pattern
                s += (s + m < n) ? m - badchar[txt.charAt(s + m)] : 1;
            } else {
                // Shift pattern so the bad character in text aligns with its last occurrence in pattern
                s += Math.max(1, j - badchar[txt.charAt(s + j)]);
            }
        }
    }

    public static void main(String[] args) {
        String txt = "ABAAABCD";
        String pat = "ABC";
        search(txt, pat);
    }
}




How It Works (Bad Character Heuristic):
Preprocess the pattern to store the last index of each character.

Start aligning the pattern with the text from right to left.

If mismatch, use the bad character rule to skip unnecessary comparisons.

If match, print the index and shift accordingly.
