题目
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1
11
21
1211
111221
1 被读作 "one 1" ("一个一") , 即 11。
11 被读作 "two 1s" ("两个一"), 即 21。
21 被读作 "one 2", "one 1" ("一个二" , "一个一") , 即 1211。
给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1:
输入: 1
输出: "1"
示例 2:
输入: 4
输出: "1211"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-and-say
解答
- 直推法
类似斐波拉契,知道前几项找关系得到后项
def countAndSay(n):
if n == 1:
res = '1'
if n == 2:
res ='11'
pre = '11'
for i in range(3, n+1):
res = ''
cnt = 1 #
length = len(pre) # 知道上一级的len,遍历每个字符才能得到下一级的res
for j in range(1, length): # use range(1,) other than range(0,) in case out of range
if pre[j-1] == pre[j]:
cnt += 1
# res += str(cnt) + pre[j-1]
# if this order exists,
# the way to output last one(last two nums different)number
# or both last two(last two nums same)numbers are different
else:
res += str(cnt) + pre[j-1] # one x (x is the num should be transfer)
cnt = 1
res += str(cnt) + pre[j] # must write this order beyond loop
pre = res
return res
其实中心思想就是分两种情况考虑,一种是相连的数相同,一种是相连的数不相同。下一个报数就是上一个报数的变形。如果一个报数中相邻两个数字不同,变形后原来两数会变成四个数;如果两数相同,变形后仍然是两位数,如果连续三数或者更多数相同,变形后维持两数。
如果相邻两数相同,cnt++,res里加上cnt和要变形的这个数;如果不同,cnt=1,res里加上cnt和要变形的数。
一开始有点没懂为什么在里面的for循环外要加上res += str(cnt) + pre[j-1]
而在pre[j-1] == pre[j]:
中不加上这句话。
这主要针对最后两个数字。
若最后两数不同,则最后一个数(1)在for循环外变形(one 1)-> ‘11’;
若最后两(多)数相同,则最后两(多)个数在for循环内增加cnt的值,然后在for循环外一起变形。
- 递归
每一个countAndSay(n)会得到一个报数,然后被下一个countAndSay(n+1)使用。
def countAndSay(self, n: int) -> str:
if n == 1:
return '1'
# 每次迭代都要初始化pre cnt res
pre = '' # 设初值,第一个数无法判断和后面的数是否相等
cnt = 0 # 则第一个数字是和空字符比较,因为前方没有数字能比较
res = ''
for num in self.countAndSay(n-1):
if num != pre: # 比较后一个数字和前一个数字是否相等
if cnt > 0:
res += str(cnt) + str(pre)
cnt = 1 # 除了num = 第一个数时cnt = 0,其他时候都 = 1
pre = num # pre变成等会要变形数字的上一个数字
else:
cnt += 1
res += str(cnt) + str(pre) # 为了最后一个数或两个数的变形
return res
- groupby方法