Richard's answer在许多情况下都可以很好地工作,但如果字符串W中有许多段,每个段可以以多种不同的方式分解,则可能需要指数时间。例如,假设W是abcabcabcd
,其他单词是ab
、c
、a
和bc
。那么W的前3个字母可以被分解为ab|c
或a|bc
...接下来的3个字母也是如此,以此类推,总共有2^3=8种可能的分解方式。
a|bc|a|bc|a|bc
a|bc|a|bc|ab|c
a|bc|ab|c|a|bc
a|bc|ab|c|ab|c
ab|c|a|bc|a|bc
ab|c|a|bc|ab|c
ab|c|ab|c|a|bc
ab|c|ab|c|ab|c
所有这些部分分解都必然失败,因为输入中没有包含W的最后一个字母d的单词--但在发现这一点之前,他的算法将探索它们所有。通常情况下,由n个abc的副本和一个单独的d组成的单词将花费O(n*2^n)的时间。
我们可以通过记录有关W后缀可分解性的额外信息来将其改进为O(n^2)的最坏情况时间(以O(n)空间为代价)--也就是说,我们已经发现我们可以或不能匹配到单词序列的W的后缀。这种类型的算法称为动态规划。
我们需要满足某些单词W可分解的条件,即W以其他单词集合中的某个单词X开头,并且从位置|X|+1开始的W后缀是可分解的。 (我在这里使用基于1的索引,并用S [i..j]表示字符串S的子字符串,其起始位置为i,结束位置为j。)
每当我们发现当前单词W的后缀从某个位置i开始是可分解的或不可分解的时,我们可以记录这一事实并稍后利用它来节省时间。例如,在测试前8个分解中的前4个后,我们知道从位置4(即
abcabcd
)开始的W的后缀是
不可分解的。那么当我们尝试第5个分解时,即以
ab
开头的第一个分解时,我们首先问自己:W的剩余部分,即从位置3开始的后缀是否可分解?我们还不知道,因此我们尝试添加
c
以得到
ab|c
,然后我们问:W的剩余部分,即从位置4开始的后缀是否可分解?我们发现它已经被发现是不可分解的,因此我们可以立即得出结论,即以
ab|c
开头的W的任何分解也是不可能的,而不必通过所有4种可能性。
假设当前单词W是固定的,我们想要构建一个函数f(i),该函数确定从位置i开始的W后缀是否可分解。伪代码可能如下所示:
- Build a trie the same way as Richard's solution does.
- Initialise the array KnownDecomposable[] to |W| DUNNO values.
f(i):
- If i == |W|+1 then return 1. (The empty suffix means we're finished.)
- If KnownDecomposable[i] is TRUE or FALSE, then immediately return it.
- MAIN BODY BEGINS HERE
- Walk through Richard's trie from the root, following characters in the
suffix W[i..|W|]. Whenever we find a trie node at some depth j that
marks the end of a word in the set:
- Call f(i+j) to determine whether the rest of W can be decomposed.
- If it can (i.e. if f(i+j) == 1):
- Set KnownDecomposable[i] = TRUE.
- Return TRUE.
- If we make it to this point, then we have considered all other
words that form a prefix of W[i..|W|], and found that none of
them yield a suffix that can be decomposed.
- Set KnownDecomposable[i] = FALSE.
- Return FALSE.
调用 f(1) 然后告诉我们 W 是否可分解。
在调用 f(i) 返回时,KnownDecomposable[i] 已经赋值为非 DUNNO 值 (TRUE 或 FALSE)。仅当 KnownDecomposable[i] 为 DUNNO 时,才会执行函数的主体。这些事实意味着函数的主体只会运行函数可以被调用的不同值 i 的次数。这些值最多有 |W|+1 个,即 O(n),除递归调用外,对 f(i) 的调用最多需要 O(n) 的时间来遍历 Richard's trie,因此总的时间复杂度受到 O(n^2) 的限制。
test,tester
,输出应该是 tester 吗?或者如果输入是:abc,bcdef
,输出应该是什么? - Nishant Kumartestertest
作为子字符串? - piotrekg2