Text replacement with Räubersprache ("Røversprog")
I probably would reach directly for Parsec or another parsing library writing this from scratch, but I think even with a more direct recursive solution you've drastically overcomplicated this!
Separate your problem into constituent pieces which you find easier to handle on their own terms. In this case, I would start by writing a function phoneme :: [Multigraph] -&gt; String -&gt; (Phoneme, String) to consume only a single n-graph off of a given word.
import Data.List (isPrefixOf, stripPrefix)

type Phoneme = String
type Multigraph = Phoneme

phoneme :: [Multigraph] -&gt; String -&gt; Maybe (Phoneme, String)
phoneme multigraphs word@(letter:rest) = -- The use of @ is an example of an 'as-pattern'
    case filter (`isPrefixOf` word) multigraphs of
      (multigraph:_) -&gt; Just (multigraph, fromJust $ stripPrefix multigraph word)
      []             -&gt; Just ([letter], rest)
phoneme _multigraphs [] = Nothing        -- Prefixing an arg with _ says to the compiler
                                         -- that you meant not to use it in the definition
                                         -- i.e. you won't get a warning when compiling
                                         -- with -Wall (specifically --Wunused-matches)

This version achieves multigraph detection by testing the passed multigraphs against the remaining portion of the string, \$O(m)\$ in the number of multigraphs. Your original version tests every multigraph against every prefix of the string, making it \$O(mn)\$.
Now the next obvious step is to use this function in breaking an entire string into its constituent phonemes.
import Data.List (unfoldr)

phonemes :: [Multigraph] -&gt; String -&gt; [Phoneme]
phonemes multigraphs = unfoldr (phoneme multigraphs)

Sometimes you either know about a common Haskell concept or you don't. In this case, if you're unfamiliar, unfoldr :: (b -&gt; Maybe (a, b)) -&gt; b -&gt; [a] repeatedly applies a function that returns a desired output and some state to itself, collecting the output into a list and terminating once the function reaches the “end” and has returned Nothing.
Now before anything else we have to be able to robber an individual phoneme.
robberPhoneme :: [Multigraph] -&gt; [Multigraph] -&gt; Phoneme -&gt; String
robberPhoneme _mconsonants mvowels p
  | p `elem` mvowels = p
  | p `elem` vowels  = p
  | otherwise        = p ++ &quot;o&quot; ++ p
  where vowels = map pure &quot;aeiouy&quot;

You could obviously leave out the consonants argument, but I think it's helpful to remind callers not to just pass all of the multigraphs they have on hand, vowels being treated differently from consonants.
And now we're ready to tie it together.
robber :: [Multigraph] -&gt; [Multigraph] -&gt; String -&gt; String
robber mconsonants mvowels = concatMap (robberPhoneme mconsonants mvowels)
                           . phonemes (mconsonants ++ mvowels)

And there you go! Much less bookkeeping, parsed strictly left to right, and by relying entirely on standard functions for all of the recursion anyone reading my code (including me, later) can quickly understand how control flows through the program without having to mentally grapple with primitive recursion.
This doesn't sit wholly perfectly with me (I'd probably create a datatype to represent all the different kinds of phonemes in a language and pass that around instead, I didn't feel good just throwing the monophthongs in there but it was expedient, couple other nits) but it's enough of a solution that I'd ship it.