Alfred V. Aho 2026-11-05
Its useful distinction is between text progress and pattern progress. On a failed extension, a failure link changes the candidate pattern prefix without moving backward in the text. Shared prefixes and suffix continuations avoid repeating a complete independent search for every keyword, while reporting cost still grows with the number of actual matches.
End-position equivalence 2026-11-05
Two nonempty substrings have end-position equivalence when their sets of occurrence-ending positions in one fixed text are equal. This is the grouping used for states of a suffix automaton, with an additional initial state. In ababa, using one-based positions, b and ab both end at , whereas a ends at . Thus b and ab share a state despite their different lengths.[ref]
Equality of ending sets ensures that appending the same character gives equal new ending sets whenever the extension occurs. This makes character transitions well defined. Within a class the represented strings are nested suffixes, one at every length in an interval; suffix links connect that interval to the next shorter class.
Margaret J. Corasick 2026-11-05
Margaret J. Corasick coauthored the 1975 multi-keyword matching algorithm with Alfred V. Aho. Their Aho-Corasick algorithm builds a finite-state keyword-matching machine and scans the text once, reporting occurrences of all dictionary patterns.[ref]
A trie shares pattern prefixes, while failure links retain the longest useful suffix after a mismatch. For patterns he and she, reaching the end of she must also report he; terminal suffix information is therefore part of matching, not merely following trie edges. The mechanism makes one text scan reusable across many search terms.
Output link 2026-11-05
An output link in an Aho-Corasick automaton points to the nearest terminal state encountered strictly after the current state on its failure link chain. It skips states that do not end a pattern string. After reading a text character, report a pattern at the current state if terminal, then follow output links to report its terminal suffixes.
For dictionary strings a, ba, aba, reaching state aba reports all three patterns ending at that position. Its output link reaches ba, and the next reaches a. Reporting only aba would miss two valid occurrences. The current state's own match is handled separately because an output link points to a proper suffix, not to itself.
If is the failure link destination, the output link is when that state is terminal, and otherwise the output link of . Build this information in increasing trie depth, so the shorter failure states are already available. Traversing only terminal links makes reporting cost proportional to the number of emitted matches, rather than the length of every nonterminal failure chain.[ref]
Use the following context-free grammar, with start symbol :
The symbol generates the balanced members of the suffix-nonnegative binary language, while permits extra zeros. To prove the claim and prepare the ambiguity argument, reverse every generated string. The reversed productions are
Here represents the reversals of the strings, and represents the reversals of the strings. A string belongs to the desired formal language precisely when its reversal has no string prefix with more ones than zeros. Associate a path with each reversed string, counting a zero as a step and a one as a step .
The nonterminal generates exactly the paths that stay nonnegative and finish at zero. The production consists of an initial up-step, a balanced path one level higher, a down-step to zero, and another balanced path. Conversely, any nonempty balanced nonnegative path has a first return to zero, and splitting there gives exactly this form. Mathematical induction on length proves the characterization.
The productions for generate nonnegative paths: stays strictly above zero after its first step, while first executes a balanced excursion and then another nonnegative path. Conversely, a nonempty nonnegative path starts with zero. If it never returns to height zero, removing that initial zero leaves a nonnegative path, giving . If it does return, split at its first return to obtain . These shorter components can be generated by mathematical induction. This proves that the original context-free grammar generates exactly the required formal language.
For instance, in the original context-free grammar,
while . The string is excluded since its entire length is a string suffix having two ones and only one zero.
Scan the two suffixes until a mismatch or an endpoint:
def longestFrom(i: Int, j: Int): Int = {
  require(0 <= i && i <= N && 0 <= j && j <= N)
  var k = 0
  while (k < N - i && k < N - j && a(i + k) == a(j + k)) {
    k += 1
  }
  k
}
The loop invariant is and equality of the two array segments of length . The loop variant is , which decreases whenever the while loop runs. Short-circuit evaluation checks both bounds before indexing. On exit either a string suffix has ended or the next characters differ, so no longer common string prefix is possible. Thus the returned value is the longest common prefix length. Overlap is allowed: the code only reads the array, so the two matching segments need not occupy disjoint storage.
All suffixes beginning with a fixed string form an interval in lexicographic order. To see this, suppose two suffixes start with a string , while a string suffix between them fails to start with . At its first differing character it must be either smaller than both or larger than both; if it ends before does, it is smaller than both. Either possibility contradicts its position between them.
Now take a longest repeated substring . Its two occurrences are prefixes of two distinct suffixes. Every string suffix between them also begins with , so at least one adjacent pair in that interval has a common string prefix of length at least . A greater length would contradict maximality of . Therefore the maximum common-prefix length over adjacent sorted suffixes equals the longest repeated-substring length. If the maximum is zero, any adjacent pair still attains it. The argument does not assume nonoverlapping occurrences.
Figure 1.
Original suffix-array example for banana: an interval of suffixes shares the repeated prefix ana
. Details.
Reuse the verified longest-common-prefix scan, handling equality before scanning:
def before(i: Int, j: Int): Boolean = {
  require(0 <= i && i < N && 0 <= j && j < N)
  if (i == j) false
  else {
    val k = longestFrom(i, j)
    if (j + k == N) false
    else if (i + k == N) true
    else a(i + k) < a(j + k)
  }
}
There is no additional while loop: the helper's loop invariant is that the first k characters agree and both indices remain in bounds. If the second string suffix ends first, the first cannot precede it; if the first ends first, it is a proper string prefix and precedes the second. Otherwise the first mismatching characters decide lexicographic order. Distinct suffixes cannot both end after the same number of characters, since their lengths differ.
The i == j case ensures a strict comparator, returning false for a string suffix compared with itself, in constant time. For distinct suffixes its running time is , where is the longest repeated-substring length of the full array.
Sort string suffix starting positions, then examine only adjacent entries:
def longestRepeated(): (Int, Int, Int) = {
  require(N >= 2)
  val suffixes = (0 until N).toArray.sortWith(before)
  var besti = suffixes(0)
  var bestj = suffixes(1)
  var bestk = 0
  var t = 1
  while (t < N) {
    val i = suffixes(t - 1)
    val j = suffixes(t)
    val k = longestFrom(i, j)
    if (k > bestk) {
      besti = i; bestj = j; bestk = k
    }
    t += 1
  }
  (besti, bestj, bestk)
}
The while loop retains a best triple among adjacent pairs already visited. All string suffix starting positions are distinct, and the adjacent-suffix characterization proves that considering these pairs suffices. The two definitions named longestRepeated in parts b and e are alternatives, not duplicate members of one software class.
Assuming a comparison sort using comparator calls, sorting takes time, because each comparison scans characters. The final pass takes . Thus overall time is , at worst , with auxiliary storage for indices and ordinary sorting workspace. Merely storing indices does not make string suffix comparisons constant-time; claiming here would omit their cost. Faster suffix-array constructions require additional machinery beyond this comparator-based solution.
Use null to indicate that the requested key is absent:
private def find(name: String, head: Entry): Entry = {
  var cursor = head
  while (cursor != null && cursor.name != name) {
    cursor = cursor.next
  }
  cursor
}
The loop invariant is that the skipped string prefix contains no entry with the requested name and cursor starts the unsearched string suffix of the unchanged bucket. Its remaining length is a loop variant. Short-circuit evaluation prevents accessing a null entry. On exit, either cursor is null and the whole bucket has been ruled out, or its name equals the key. No list links or stored numbers are modified. String comparison here uses value equality; node deletion elsewhere uses reference identity.
Proper border of a string 2026-11-05
A proper border of a string is a string prefix that is also a string suffix and is shorter than the complete string. With zero-based lengths, its length satisfies for a length- string, if the empty string is allowed. Specify that convention when counting borders.
For ababa, the nonempty proper borders are a and aba; the complete ababa is a border of a string but not a proper one. A proper border of length allows two copies of the string to overlap with displacement . The border aba therefore explains the shift of two between overlapping occurrences of ababa in abababa.
The prefix function stores the longest proper-border length of each prefix. For ababa, following these lengths gives , listing successively shorter candidate overlaps. The Knuth-Morris-Pratt algorithm uses this chain after a mismatch, retaining a suffix already known to match rather than comparing the whole pattern again.
String suffix Created 2026-10-20 Updated 2026-10-25
A string suffix consists of all characters from some position to the end of a string. For example, TAC has suffixes TAC, AC, C and the empty string. Sorting these in lexicographic order groups all suffixes beginning with a given query into one interval.
Suffix link 2026-11-05
In a suffix automaton, a suffix link from state leads to the state containing the longest suffix of its longest represented string that lies in a different end-position equivalence class. If is the maximum represented length, state represents one string at each length from through . Following links strictly decreases length and eventually reaches the initial state.[ref]
For ababa, the state representing ababa also represents baba; its link leads to the state containing aba. This skips a suffix already in the current class. The links form a rooted tree useful for propagating occurrence counts from longer matches to shorter suffixes.