要解决的问题:
给定非空字符串s和包含列表的字符串数组wordArr 非空单词,确定s是否可以分割为a 空格分隔的一个或多个字典单词的序列。你可以 假设字典不包含重复的单词。
例如,给定s =“leetcode”,wordArr = [“leet”,“code”]。
返回true,因为“leetcode”可以被分割为“leet code”。
在上面的问题中,是否可以构建一个包含wordArr
中每个字符串的trie。然后,对于给定字符串s
中的每个字符,请按照特里结构进行操作。如果trie分支终止,则此子字符串完成,因此将剩余的字符串传递给根,并以递归方式执行完全相同的操作。
这应该是O(N)时间和O(N)空间是否正确?我问,因为我正在研究的问题是以最优的方式这将是O(N ^ 2)时间,我不确定我的方法有什么问题。
例如,如果s = "hello"
和wordArr = ["he", "ll", "ee", "zz", "o"]
,则"he"
将在trie的第一个分支中完成,"llo"
将以递归方式传递给根。然后,"ll"
将完成,因此"o"
会传递到特里的根。然后"o"
完成,这是s
的结束,所以返回true。如果s
的结尾未完成,则返回false。
这是对的吗?
答案 0 :(得分:1)
你的例子确实会提出线性时间复杂度,但请看这个例子:
s = "hello"
wordArr = ["hell", "he", "e", "ll", "lo", "l", "h"]
现在,首先尝试“地狱”,但是在下一个递归周期中,没有找到解决方案(没有“o”),所以算法需要回溯并假设“地狱”不合适(双关语不是),所以你尝试“他”,并在下一级别找到“ll”,但然后它再次失败,因为没有“o”。再次需要回溯。现在从“h”开始,然后是“e”,然后再次出现故障:你尝试“ll”没有成功,所以回溯使用“l”代替:解决方案现在可用:“hel lo”。
所以,没有这个没有 O(n)时间复杂度。
答案 1 :(得分:0)
我怀疑这个问题是回溯的问题。如果单词基于特定字典不可分段,或者如果有多个可能的子字符串具有公共前缀,该怎么办?例如,假设字典包含he
,llenic
和llo
。在trie的一个分支上失效将需要回溯,一些相应的时间复杂度增加。
这类似于正则表达式匹配问题:您提供的示例就像测试输入词一样
^(he|ll|ee|zz|o)+$
(任意数量的字典成员,任何顺序,没有别的)。我不知道正则表达式匹配的时间复杂性,但我知道回溯可以让你进入serious time trouble。
我确实找到了this answer,其中说:
对字符串运行DFA编译的正则表达式确实是O(n),但最多可能需要O(2 ^ m)构造时间/空间(其中m =正则表达式大小)。
所以也许是O(n ^ 2),减少了施工工作量。
答案 2 :(得分:0)
让我们首先将trie转换为nfa。我们在根上创建一个接受节点,并添加一条边,该边从trie中字典的每个单词末尾移动到空char的根节点。
时间复杂度:由于trie中的每个步骤,我们只能移动到表示输入字符串和根中当前char的一个边。 T(n)= 2×T(n-1)+ c 这给了我们O(2 ^ n)
确实不是O(n),但你可以使用动态编程做得更好。
这个想法是只计算一次单词的每个后缀。我们只有n个后缀,最终会得到O(n ^ 2)。
代码格式algorithms.tutorialhorizon.com:
Map<String, String> memoized;
Set<String> dict;
String SegmentString(String input) {
if (dict.contains(input)) return input;
if (memoized.containsKey(input) {
return memoized.get(input);
}
int len = input.length();
for (int i = 1; i < len; i++) {
String prefix = input.substring(0, i);
if (dict.contains(prefix)) {
String suffix = input.substring(i, len);
String segSuffix = SegmentString(suffix);
if (segSuffix != null) {
memoized.put(input, prefix + " " + segSuffix);
return prefix + " " + segSuffix;
}
}
你可以做得更好!
Map<String, String> memoized;
Trie<String> dict;
String SegmentString(String input)
{
if (dict.contains(input))
return input;
if (memoized.containsKey(input)
return memoized.get(input);
int len = input.length();
foreach (StringBuilder word in dict.GetAll(input))
{
String prefix = input.substring(0, word.length);
String suffix = input.substring(word.length, len);
String segSuffix = SegmentString(suffix);
if (segSuffix != null)
{
memoized.put(input, word.ToString() + " " + segSuffix);
return prefix + " " + segSuffix;
}
}
retrun null;
}
使用Trieto只有在Trie到达单词结尾时才能找到递归调用,你会得到o(z×n),其中z是Trie的长度。