|
1 | 1 | package com.thealgorithms.others;
|
2 | 2 |
|
3 |
| -/** |
4 |
| - * @author Prateek Kumar Oraon (https://github.com/prateekKrOraon) |
5 |
| - */ |
6 | 3 | import java.util.Scanner;
|
| 4 | +import java.util.Set; |
| 5 | +import java.util.TreeSet; |
7 | 6 |
|
8 |
| -// An implementation of string matching using finite automata |
| 7 | +/** |
| 8 | + * A class to perform string matching using <a href="https://en.wikipedia.org/wiki/Finite-state_machine">finite automata</a>. |
| 9 | + * |
| 10 | + * @author <a href="https://github.com/prateekKrOraon">Prateek Kumar Oraon</a> |
| 11 | + */ |
9 | 12 | public final class StringMatchFiniteAutomata {
|
| 13 | + |
| 14 | + // Constants |
| 15 | + private static final int CHARS = 256; // Total number of characters in the input alphabet |
| 16 | + |
| 17 | + // Finite automata table |
| 18 | + private static int[][] finiteAutomata; |
| 19 | + |
| 20 | + // Private constructor to prevent instantiation |
10 | 21 | private StringMatchFiniteAutomata() {
|
11 | 22 | }
|
12 | 23 |
|
13 |
| - public static final int CHARS = 256; |
14 |
| - public static int[][] fa; |
15 |
| - public static Scanner scanner = null; |
16 |
| - |
17 | 24 | public static void main(String[] args) {
|
18 |
| - scanner = new Scanner(System.in); |
19 |
| - System.out.println("Enter String"); |
20 |
| - String text = scanner.nextLine(); |
21 |
| - System.out.println("Enter pattern"); |
22 |
| - String pat = scanner.nextLine(); |
| 25 | + // Scanner instance for user input |
| 26 | + try (Scanner scanner = new Scanner(System.in);) { |
| 27 | + |
| 28 | + System.out.println("Enter text:"); |
| 29 | + String text = scanner.nextLine(); |
23 | 30 |
|
24 |
| - searchPat(text, pat); |
| 31 | + System.out.println("Enter pattern:"); |
| 32 | + String pattern = scanner.nextLine(); |
25 | 33 |
|
26 |
| - scanner.close(); |
| 34 | + Set<Integer> indexFound = searchPattern(text, pattern); |
| 35 | + indexFound.forEach(System.out::println); |
| 36 | + } |
27 | 37 | }
|
28 | 38 |
|
29 |
| - public static void searchPat(String text, String pat) { |
30 |
| - int m = pat.length(); |
31 |
| - int n = text.length(); |
| 39 | + /** |
| 40 | + * Searches for the pattern in the given text using finite automata. |
| 41 | + * |
| 42 | + * @param text The text to search within. |
| 43 | + * @param pattern The pattern to search for. |
| 44 | + */ |
| 45 | + public static Set<Integer> searchPattern(String text, String pattern) { |
| 46 | + Set<Integer> indexFound = new TreeSet<>(); |
| 47 | + int patternLength = pattern.length(); |
| 48 | + int textLength = text.length(); |
| 49 | + |
| 50 | + // Initialize finite automata table |
| 51 | + finiteAutomata = new int[patternLength + 1][CHARS]; |
32 | 52 |
|
33 |
| - fa = new int[m + 1][CHARS]; |
| 53 | + // Preprocess the pattern to create the finite automata table |
| 54 | + computeFiniteAutomata(pattern, patternLength); |
34 | 55 |
|
35 |
| - computeFA(pat, m, fa); |
| 56 | + int state = 0; // Initial state |
36 | 57 |
|
37 |
| - int state = 0; |
38 |
| - for (int i = 0; i < n; i++) { |
39 |
| - state = fa[state][text.charAt(i)]; |
| 58 | + // Process the text over the finite automata |
| 59 | + for (int i = 0; i < textLength; i++) { |
| 60 | + state = finiteAutomata[state][text.charAt(i)]; |
40 | 61 |
|
41 |
| - if (state == m) { |
42 |
| - System.out.println("Pattern found at index " + (i - m + 1)); |
| 62 | + if (state == patternLength) { |
| 63 | + indexFound.add((i - patternLength + 1)); |
43 | 64 | }
|
44 | 65 | }
|
| 66 | + return indexFound; |
45 | 67 | }
|
46 | 68 |
|
47 |
| - // Computes finite automata for the pattern |
48 |
| - public static void computeFA(String pat, int m, int[][] fa) { |
49 |
| - for (int state = 0; state <= m; ++state) { |
| 69 | + /** |
| 70 | + * Computes the finite automata table for the given pattern. |
| 71 | + * |
| 72 | + * @param pattern The pattern to preprocess. |
| 73 | + * @param patternLength The length of the pattern. |
| 74 | + */ |
| 75 | + private static void computeFiniteAutomata(String pattern, int patternLength) { |
| 76 | + for (int state = 0; state <= patternLength; ++state) { |
50 | 77 | for (int x = 0; x < CHARS; ++x) {
|
51 |
| - fa[state][x] = getNextState(pat, m, state, x); |
| 78 | + finiteAutomata[state][x] = getNextState(pattern, patternLength, state, x); |
52 | 79 | }
|
53 | 80 | }
|
54 | 81 | }
|
55 | 82 |
|
56 |
| - public static int getNextState(String pat, int m, int state, int x) { |
57 |
| - // if current state is less than length of pattern |
58 |
| - // and input character of pattern matches the character in the alphabet |
59 |
| - // then automata goes to next state |
60 |
| - if (state < m && x == pat.charAt(state)) { |
| 83 | + /** |
| 84 | + * Gets the next state for the finite automata. |
| 85 | + * |
| 86 | + * @param pattern The pattern being matched. |
| 87 | + * @param patternLength The length of the pattern. |
| 88 | + * @param state The current state. |
| 89 | + * @param x The current character from the input alphabet. |
| 90 | + * @return The next state. |
| 91 | + */ |
| 92 | + private static int getNextState(String pattern, int patternLength, int state, int x) { |
| 93 | + // If the current state is less than the length of the pattern |
| 94 | + // and the character matches the pattern character, go to the next state |
| 95 | + if (state < patternLength && x == pattern.charAt(state)) { |
61 | 96 | return state + 1;
|
62 | 97 | }
|
63 | 98 |
|
| 99 | + // Check for the highest prefix which is also a suffix |
64 | 100 | for (int ns = state; ns > 0; ns--) {
|
65 |
| - if (pat.charAt(ns - 1) == x) { |
| 101 | + if (pattern.charAt(ns - 1) == x) { |
| 102 | + boolean match = true; |
66 | 103 | for (int i = 0; i < ns - 1; i++) {
|
67 |
| - if (pat.charAt(i) != pat.charAt(state - ns + i + 1)) { |
| 104 | + if (pattern.charAt(i) != pattern.charAt(state - ns + i + 1)) { |
| 105 | + match = false; |
68 | 106 | break;
|
69 | 107 | }
|
70 |
| - |
71 |
| - if (i == ns - 1) { |
72 |
| - return ns; |
73 |
| - } |
| 108 | + } |
| 109 | + if (match) { |
| 110 | + return ns; |
74 | 111 | }
|
75 | 112 | }
|
76 | 113 | }
|
77 | 114 |
|
| 115 | + // If no prefix which is also a suffix is found, return 0 |
78 | 116 | return 0;
|
79 | 117 | }
|
80 | 118 | }
|
0 commit comments