Вы можете использовать динамическое программирование напрямую.
Нам дана строка s с n буквами. Нам дан набор кусков P = {p_1, ..., p_k}. Каждая часть имеет одну букву в передней части p_i.f и одну в задней части p_i.b.
Обозначим через f (j, p) функцию, которая возвращает true, если возможно создать подстроку s_1 ... s_j, используя кусочки в p \ subseteq P, и false в противном случае.
Имеет место следующее повторение:
f (n, P) = f (n-1, P-p_1) | f (n-1, P-p_2) | ... | f (n-1, P-p_k)
В простом английском языке выполнимость s, использующего все куски в P, зависит от выполнимости подстроки s_1 ... s_n-1, заданной на один кусочек меньше, и мы пытаемся удалить все возможные кусочки (конечно, на практике мы не делаем необходимо удалить все фрагменты по одному, нам нужно удалить только те фрагменты, для которых p_i.f == s_n || p_i.b == s_n).
Исходным условием является то, что f (1, P-p_1) = f (1, P-p2) = ... = true, при условии, что мы уже проверили a-priori (в линейном времени), что достаточно буквы в P, чтобы покрыть все буквы в с.