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 <= 10001 <= words[i].length <= 16word[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】协议,转载请保留原文出处及本版权声明。











评论