原题链接
一、题目描述
如果一个质数 P 的每位数字都是质数, 而且每两个相邻的数字组成的两位 数是质数, 而且每三位相邻的数字组成的三位数是质数, 依次类推, 如果每相 邻的 k 位数字组成的 k 位数都是质数, 则 P 称为超级质数。
如果把超级质数 P 看成一个字符串, 则这个超级质数的每个子串都是质 数。
例如, 53 是一个超级质数。
请问, 最大的超级质数是多少?
简单来说就是判断一个数字所有子串是否是质数。
由题可知,使用暴力枚举判断任意一个数是否是超级质数的复杂度大约
O( ( n 2 + n ) − 3 2 (n^2+n)^\frac{-3}{2} (n2+n)2−3)
显然暴力枚举行不通。
二、思路
使用[2,3,5,7]四个数生成数字,将生成的数字按其位数分成不同阶段,个位数是一个阶段,十位数是一个阶段,每个阶段符合超级质数要求的数字都放入prime数组中,prime[i][j]表示 i 阶段产生的集合中的第 j 个元素。
当前阶段用于判断的数字从上一个阶段的集合中与[2,3,5,7]组合生成,如此便保证了当前阶段数字的前n-1位是超级质数,再判断后n-1位数是否在上一个阶段的集合中,如果是,即可确定其是当前阶段的超级质数。
举个例子,设abcd是四位数的超级质数,abcde是新生成的数,a,b,c,d,e∈[2,3,4,5],由于已知道abcd是超级质数,那么abcde前n-1位的子串都是质数,现在只需要判断abcde整体是否是质数,和bcde是否是超级质数(其子串是否是质数)。遍历prime[i-1],若bcde == prime[i-1][j]即abcde为本阶段超级质数并保存到当前阶段的集合中。
1.由[2,3,5,7]组合出的两位数质数必然是超级素数。
2.prime[i]集合中保存了该阶段所有的超级质数。
3.i从0开始,prime[i]表示i+1位数的集合,即当i=1时,prime[i]表示2位超级质数的集合。
三、代码
代码如下(示例):
import math
# 判断质数
def isPrime(n) -> bool:
for i in range(2,int(math.sqrt(n))+1):
if not n%i:
# 返回两个参数是为了方便调试
return False,i
return True,1
# 判断质数优化版
def isPrimeUp(n) -> bool:
if n%2 == 0:
return False,2
for i in range(3,int(math.sqrt(n))+1,2):
if n%i == 0:
return False,i
return True,1
# 判断其后n-1个字符串是否是超级质数
def substrIsP(n : int,sub : str):
if str(n)[-len(sub):] == sub:
return True
return False
def bfs(pa = [2,3,5,7]):
# 每次当前阶段只使用上一个阶段的超级质数,所以使用两个列表交替即可
Previous = [str(_) for _ in pa]
present = []
# 初始化队列
q = pa.copy()
# 记录每一个阶段的数量
numberStages = len(q)
cut = 0
# 队列不为空则一直执行
while q:
# 成立则进入下一个阶段
if cut == numberStages:
cut = 0
numberStages = len(q)
# 交换:Previous=present,present = []
Previous,present = present,[]
# 取出队列中的元素
element = q.pop(0)
cut+=1
# 对元素进行组合与判断
for i in pa:
num = element*10+i
# 首先num需要是质数,其次它的子串也是质数
if not isPrime(num)[0]:
continue
# 将上一阶段产生的质数用来判断本阶段质数的后n-1子串是否是超级质数
for j in Previous:
if substrIsP(num,j):
# 成立的数值存入队列与新阶段
q.append(num)
present.append(str(num))
break
# 最后一个元素必然是最大的超级质数
else:
return element
if __name__ == '__main__':
print(bfs()) # 373
总结
个人感觉不是考察质数的判断,而是对字符串及子串的理解。