def get_characters_mapping(X, f=None):
    """Determines all unique characters from the dataset.
    
    :param X: List of tokenized sentences.
    :param f: Preprocessing functions applied to every token before analysis.
    This function can be different from the one used with get_vocabulary
    :return dict mapping characters to their IDs. Character 0 is always padding
    and 1 is always UNK
    """
    f = f or (lambda x: x)
    
    vocab = {
        '<pad>': 0,
        '<unk>': 1,
    }
    for sentence in X:
        for word in sentence:
            for letter in f(word):
                if letter not in vocab:
                    vocab[letter] = len(vocab)
    return vocab