LeetCode 1048. Longest String Chain
@(LeetCode)
问题描述
给定一个单词列表,每个单词包含小写英文字母。
如果能在单词 word1
的任一位置插入一个字符,使得其与 word2
相等,我们把 word1
称之为 word2
的前驱。比如 abc
是 abac
的前驱。
一个单词链是指一组单词 [word_1, word_2, ..., word_k]
,其中 k > 1
,满足 word_1
是 word_2
的前驱,word_2
是 word_3
的前驱,以此类推。
要求从给定的单词列表 words
中,返回最长的单词链。
栗 1:
注意:
1 <= words.length <= 1000
1 <= words[i].length <= 16
word[i] 只包含小写英文字母
解题思路
由题意可知,如果 word1
是 word2
的前驱,那么 word1
的长度必定比 word2
小 1
。
那么单词链接的长度只可能是: len -> len+1 -> len+2 -> ...
,依次递增。
所以很容易想到的做法是将单词以长度进行分组,如下所示。
但想要得到最大的链接长度,就得计算出所有单词的链接长度,取最大值。
但是每个单词的前驱可能有多个,那么需要计算出其所有可能的链接组合长度,再比较得出该单词的最大链接长度。
所以此题的关键点在于:求出单词的最大链接长度,即计算每个单词所有可能的链接长度,取其最大值。这里我们将单词长度分组以层的概念进行描述,每层的单词长度相同。
下面我们来介绍两种解法。
解法 1
很显然,以最后一层单词为起点的链接长度肯定为 1
,因为其后没有可链接的单词。
在倒数第二层中,若单词 w1
是最后一层中单词 w2
的前驱,则其可能存在的长度为 2
,否则为 1
。
在倒数第三层中,若单词 w1
是倒数第二层中单词 w2
的前驱,而 w2
的链接长度可能为 2
或者 1
。
若
w2
的链接长度为2
,则长度为3
。若
w2
的链接长度为1
,则长度为2
。若
w1
不是任何单词的前驱,则长度为1
。
...
以此类推,在倒数第 i
层中,若单词 w1
是倒数第 i-1
层中单词 w2
的前驱,则其中一种可能的长度为 「w2 的链接长度 + 1
」。
而 w1
可能是倒数 i-1
层中 n
个单词的前驱,因此需要计算 n
个可能的链接长度,再取最大值。
总结成公式如下:
那么如何计算 w1
是否为 w2
的前驱呢?
只需同时遍历
w1
和w2
,如果遇到不相等的字符,则说明w1
要插入该字符,w2
的index
后移一位。若后续碰到不相等的字符,则说明不为前驱,否则为其前驱。
了解上述理论后,实现就不难了。因为我们已经将单词以长度进行分组,所以很容易得到上一层的所有单词,进行遍历计算即可。
这里,我提供了两种实现方式,非递归版和递归版。有兴趣的同学可点击链接进行查看。
解法 2
以上解法是从后往前的方式计算,即先计算长度最大的单词。
我们还可以通过另外一种方式,即从前往后。但注意这里单词的链接长度计算是以该单词结束,而解法 1
是以该单词为起点。
由于单词长度最大为 16
,即使完全穷举出某个单词的所有前驱,也只有 16
种,效率上可能会更优。解法 1
是遍历下一层的所有单词来计算。
推导公式跟解法 1
中的大同小异。
代码也比较简洁,以下是从 讨论区 copy
的 java
代码。
总结一下,这道题属于动态规划类,即在前面结果的基础上来计算当前结果。这类题目往往看起来复杂,但是推导出动态规划方程后,问题就迎刃而解了。
版权声明: 本文为 InfoQ 作者【liu_liu】的原创文章。
原文链接:【http://xie.infoq.cn/article/c4001d4aef717d744ac55713e】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论