My first attempt was to just store the list of words as a linked list, check each one to see how many entries (and (string-prefix? word p) (string-suffix? word s)) would be true for, return their corresponding indices and return the maximum one, but that quickly exceeded the time limit once the dictionary reached any appreciable size.
Instead, since each word will be checked for an affix of at most 10 characters, it ends up being faster to build a hash table of all the possible combinations of prefix and suffix for each word, writing the hashes sequentially so that later ones overwrite earlier ones, and pulling the mapped value for each call of f. (This ensures that it'll always be the maximum word index that's returned, too. No need to filter out multiple results.)
#lang racket
(define word-filter%
(class object%
(super-new)
; words : (listof string?)
(init-field
words) ; Take in the provided dictionary.
(define word-ends-hash (make-hash)) ; Make an empty hash table.
(for/list ([w (in-list words)] ; For each word in the dictionary,
[index (in-naturals)]) ; and its corresponding index,
(define len (string-length w)) ; calculate its length,
(for*/list ([head (in-range 1 (min 11 (add1 len)))] ; and for every combination of head length
[tail (in-range 1 (min 11 (add1 len)))]) ; and tail length for the given word
(hash-set! word-ends-hash ; from 1 to the max. affix length,
(list (substring w 0 head) ; set the key to the list containing
(substring w (- len tail) len)) ; the corresponding prefix and suffix
index))) ; and map it to the word's index.
; f : string? string? -> exact-integer?
(define/public (f prefix suffix) ; Return the mapped value for the affixes
(hash-ref word-ends-hash (list prefix suffix) -1)))) ; or -1 if it doesn't exist.