同题异构解决leetcode第3646题下一个特殊回文数
3646.下一个特殊回文数
难度:困难
问题描述:
给你一个整数n。
如果一个数满足以下条件,那么它被称为特殊数:
它是一个回文数。数字中每个数字k出现恰好k次。返回严格大于n的最小特殊数。
如果一个整数正向读和反向读都相同,则它是回文数。例如,121是回文数,而123不是。
示例1:
输入:n=2
输出:22
解释:
22是大于2的最小特殊数,因为它是一个回文数,并且数字2恰好出现了2次。
示例2:
输入:n=33
输出:212
解释:
212是大于33的最小特殊数,因为它是一个回文数,并且数字1和2恰好分别出现了1次和2次。
提示:
0<=n<=10**15
问题分析:
这个题目很容易让人想到用枚举算法,从给定的数向后枚举,然后检验这个数是否是一个回文数,且这个回文数中的每个数字出现次数恰好等于这个数字,直到碰到第一个符合条件的数为止,这个数即是所要求的回文数。
为此设计一个检验函数check_is_result(n),该函数用于判断一个整数是否为一个回文数,且这个回文数中的每个数字出现次数恰好等于这个数字,如果满足这些条件则返回True,否则返回False。
剩下就是利用枚举算法构造循环来解决这个问题了。
程序如下:
#检查一个整数是否是满足题目中条件的回文数:即它是一个回文数,且数字中每个数字k出现恰好k次。
def check_is_result(n):n=str(n)n_bak=n[::]if n==n[::-1]:n=list(n)t=set(n)for i in t:c=n_bak.count(i)if int(i)!=c:return Falseelse:return Trueelse:return False#主程序
n=int(input('pls input n='))
i=1
while True:n = n + 1if check_is_result(n):print(n)break
运行实例一
pls input n=1
22
运行实例二
pls input n=22
212
运行实例三
pls input n=1000000000
2888888882
通过枚举算法确实思路简单,但上面的运行实例三,运行时间很长,大约过了20分钟左右才出来答案,循环从1000000001枚举到2888888882,是一个天文数字的循环次数,且每一个数字都要进行复杂的检验,可见效率低下。
为此考虑,根据输入的数字n,直接构造大于n的最小特殊数。
构造特殊数,就要找到构造特殊数的规律。
通过枚举的方法,找到一些特殊数如下:
22 (两位)
212 333 (三位)
4444 (四位)
23332 32323 44144 55555 (五位)
244442 424424 442244 666666 (六位)
2441442 2555552 3443443 4241424 4343434 4421244 4433344 5255525 5525255 6661666 7777777 (七位)
可以看出,特殊数组成的序列,是一个固定序列,与输入的整数n没有太大的关系,只和整数n的数字位数有关,可以想到大于整数n的最小特殊数可能数字位数和n相等,或者比n的数字位数多一位,比如参照上面的特殊数序列,如果输入的n在2到21之间,输出结果都应该是22,如果输入的n在22到211之间,输出结果都应该是212。
再观察上面的特殊数序列,如果特殊数是偶数位,则全部由偶数组成这个特殊数的每一位数字,如果特殊数是奇数位,则构成特殊数的数字只能有一位奇数,其它都偶数,其中原因不难想见。
因此,可以这样来解决问题:
根据输入整数n的数字位数,把与n位数相同的特殊数以及比整数n位数多一位的特殊数也找出来,并构成一个特殊数的序列,并把整数n也加入这个序列,然后按大小排序,则比整数n大的最小特殊数不是就很容易找出了吗?
程序如下:
def one_into_array(one,my_array):a=[]n=len(my_array)for i in range(n+1):l=my_array[0:i]r=my_array[i:]b=l+[one]+ra.append(b)return adef get_all_array(my_list):n=len(my_list)if n==1:return[my_list]elif n==2:t=[my_list,my_list[::-1]]t.sort()return telse:l=my_list[0]r=my_list[1:]b=get_all_array(r)c=[]for i in b:c.extend(one_into_array(l,i))c.sort()return cdef get_o_num_list(n):a=[[2],[4],[6],[2,4],[8],[2,6],[2,8],[4,6],[4,8],[2,4,6],[6,8],[2,4,8],[2,6,8],[4,6,8],[2,4,6,8]]o_array=[]for i in a:if sum(i)==n:o_array.append(i)return o_arraydef get_j_num_list(n):a=[1,3,5,7,9]b=[]for i in a:old_list = get_o_num_list(n - i)for j in old_list:j.append(i)b.append(j)if n<10:b.append([n])return b#生成一半的回文
def get_o_palindrome(n):b_list=get_o_num_list(n)a=[]for i in b_list:x=[]for j in i:t=[j]*(j//2)x.extend(t)a.append(x)b=[]for i in a:t=get_all_array(i)b.extend(t)a=[]for i in b:t=list(map(str,i))a.append(t)b=[]for i in a:t=''.join(i)b.append(t)b=list(set(b))a=[]for i in b:t=i+i[::-1]a.append(t)return adef get_j_palindrome(n):b_list = get_j_num_list(n)# print(b_list)a = []for i in b_list:x = []for j in i:if j%2==0:t = [j] * (j // 2)x.extend(t)else:if j!=1:t=[j]*((j-1)//2)x.extend((t))else:t=[j]x.extend(t)a.append(x)b = []for i in a:if 1 in i and len(i)>1:i.remove(1)t = get_all_array(i)t=list(k+[1] for k in t)b.extend(t)else:t=get_all_array(i)b.extend(t)c=[]for i in b:t=list(map(str,i))c.append(t)a = []for i in c:t = ''.join(i)a.append(t)a=list(set(a))#['32', '441', '23', '55']b=[]for i in a:t=''if '1' in i:c=i[:len(i)-1]t=i+c[::-1]b.append(t)else:for j in ['3','5','7','9']:if j in i:c=i[::-1]t=i+j+cb.append(t)breakreturn b#主程序
n=int(input('pls input n='))
n=str(n)
n_len=len(n)
a=[]
if n_len==1:a=['1','22']
else:if n_len % 2 == 0:b = get_o_palindrome(n_len)a.extend(b)c = get_j_palindrome(n_len + 1)a.extend(c)else:c = get_j_palindrome(n_len)a.extend(c)b = get_o_palindrome(n_len + 1)a.extend(b)
a.append(n)
a=list(map(int,a))
a.sort()
i=a.index(int(n))
result=a[i+1] if a[i+1]!=a[i] else a[i+2]
print(result)
运行实例一
pls input n=0
1
运行实例二
pls input n=10000
23332
运行实例三
pls input n=123456789
234434432
运行实例四
pls input n=12345678998765432
23448888388884432
通过这种方法生成速度明显快了很多。
枚举方法的运行实例三,要20分钟左右才能得到结果,但用第二种直接构造的方法,基本上一秒钟就完成。
同题异构,展现不同的算法思维,让枯燥的数字因为你的思想而迸发出耀眼的光芒。