from typing import List
 
 
def wordBreak(s: str, dictionary: set[str], m: int) -> List[object]:
    """
    :param s: input string that needs to be segmented
    :param dictionary: set of dictionary words
    :param m: max length of a word in the dictionary
 
    :return: a list of two objects
    1. True if the string can be segmented into dictionary words, False otherwise
    2. A list of integers representing the partitioning of the string into dictionary words
 
    n = len(s)
    Time complexity: O(n * min(n, m))
    Space complexity: O(n)
 
    note: the complexity is not considering the dictionary space and lookup time
 
    """
 
    # dp[i] = true, if string s[0...i] can be segmented into dictionary words
    dp = [False for i in range(len(s) + 1)]
 
    # partition[i] = j, where s[j:i] is a dictionary word
    partition = [-1 for i in range(len(s) + 1)]
 
    # dp[0] = true, because an empty string can always be segmented.
    dp[0] = True
 
    # Iterate over the prefixes s[0...i]
    for i in range(1, len(s) + 1):
        # Iterate over the prefixes s[j...i] where j < i and i - j <= m
        for j in range(i-1, max(-1, i-m-1), -1):
            if dp[j] and s[j: i] in dictionary:
                dp[i] = True
                partition[i] = j
                break
 
    return [dp[len(s)], partition]
 
 
def print_partitioned_substrings(s, partition, i):
    if i == 0 or partition[i] == -1:
        return
    print_partitioned_substrings(s, partition, partition[i])
    print(s[partition[i]:i])
 
 
# Example
dict = set({"apple", "pen", "pineapple", "I", "like"})
m = 9  # max length of a word in the dictionary
 
for s in ["Ilikepineapple", "catsandlionsanddog"]:
    can_segment, partition = wordBreak(s, dict, m)
    if can_segment:
        print_partitioned_substrings(s, partition, len(s))
    else:
        print(f"String '{s}' cannot be segmented into dictionary words.\n")