算法通关之路
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.6 超级回文数

这是一道力扣(LeetCode)上难度级别为困难的题目,让我们一起来看一下。

题目描述(第906题)

如果一个正整数自身是回文数,而且它也是另一个回文数的平方,那么我们称这个数为超级回文数。

现在,给定两个正整数L和R(以字符串形式表示),返回包含在范围[L,R]中的超级回文数的数目。

示例

输入:L="4",R="1000"

输出:4

解释:4、9、121和484是超级回文数。

注意:676不是超级回文数:26 * 26=676,但26不是回文数。

提示

● 1 ≤len(L)≤ 18。

● 1 ≤len(R)≤18。

● L和R表示在[1,1018)范围内的整数的字符串。

● int(L)≤int(R)。

思路

暴力地对math.floor(img)到math.ceil(img)范围内的数进行遍历,并逐个判断其是否为回文数,如果是,则继续判断其平方是否是回文即可。

上面代码的时间复杂度显然已经大于O(img-img),代入题目给出的数据范围大概率会超时,需要考虑剪枝。关于如何根据题目数据范围判断算法是否可行可参考第20.1节的相关内容。

剪枝是一种非常重要的思想,本书会在第20章进行详细讲述。

为什么不直接构造一个回文数Q呢?这样就省去了判断Q是否是一个回文数的过程,直接判断Q的平方即可。

这样的话,问题被转化为如何构造回文数,其核心代码如下。

如果i是123,那么r就应该为321,Q就应该为123×100+321 % 100,即12300+21,即12321。

回文中心有两种,一种是回文中心为单个字符,一种是回文中心为两个字符,因此上面的算法并不完备,我们还需要考虑回文中心为两个字符的情况。拿上面的例子来说,就少考虑了123321这种情况。我们只需要补充这种情况即可。

代码如下所示。

这种算法的时间复杂度会比上面的稍微好一点,可以通过力扣(LeetCode)所有的测试用例,但是还有优化空间,读者不妨思考一下如何优化该算法。我也会在后面的章节带读者一步一步优化类似的题目,帮助读者打开思路。

代码

复杂度分析

● 时间复杂度:O(img),其中W为R的上限,在这道题中就是1018,而18的1/4是4.5,因此代码中循环到105是足够的。

● 空间复杂度:用seen来存储所有出现过的超级质数,因此空间复杂度为img,其中cnt为[L,R]间的超级回文数的个数,也就是问题的解。