Another suggestion, or, the same really, is to separate out the splitting/joining from the censoring logic.
The censoring functions should probably only be operating on lists of words with no punctuation or spaces, that’s a completely separate problem.
I believe there may also be mention of keeping the original non-alpha characters (spaces, punctuation), in that case what can be done is to have a function looking like this:
everything = group by whether letter
# everything = ['abc', ', !?', 'def', '\n ', 'blah']
clean_words = just the words from everything
censored_clean_words = censor(clean_words)
put all the words back between the other stuff
concatenate the results into a single string
Overall that’s bit of a circus, but the individual problems there aren’t so bad.
The general idea of that function being to take the input apart, do logic on the parts that are interesting, then put it back together.
Taking things apart and putting them back together is a pattern that fits a lot of problems. Even something like iterating through a list fits this - take it apart (iterate) do something (loop body) put it back together (append the results to a new list)