@@ -60,103 +60,74 @@ countAndSay(4) = 读 "21" = 一 个 2 + 一 个 1 = "12" + "11" = "1211"
6060
6161---
6262
63- ### 朴素解法
63+ ### 模拟
6464
65- 这是一道模拟题。
66- 67- 我们可以实现一个 ` nextString ` 方法,传入一个字符串 ` s ` ,返回该字符串对应的外观数列。
68- 69- 具体的实现,我们只需要统计字符串 ` s ` 中每一段连续出现的字符的类型和出现次数即可。
65+ 一个朴素的想法是:根据题意进行模拟,从起始条件 $k = 1$ 时 ` ans = "1" ` 出发,逐步递推到 $k = n$ 的情况,对于第 $k$ 项而言,其实就是对第 $k - 1$ 项的「连续段」的描述,而求「连续段」长度,可以使用双指针实现。
7066
7167代码:
72- ``` Java []
68+ ``` Java
7369class Solution {
7470 public String countAndSay (int n ) {
75- String s = " 1" ;
71+ String ans = " 1" ;
7672 for (int i = 2 ; i <= n; i++ ) {
77- s = nextString(s);
78- }
79- return s;
80- }
81- String nextString (String s ) {
82- StringBuilder sb = new StringBuilder ();
83- int n = s. length();
84- char [] cs = s. toCharArray();
85- char c = cs[0 ];
86- int cnt = 1 ;
87- for (int i = 1 ; i < n; i++ ) {
88- char cur = cs[i];
89- if (cur == c) {
90- cnt++ ;
91- } else {
92- sb. append(cnt);
93- sb. append(c);
94- 95- c = cur;
96- cnt = 1 ;
73+ String cur = " " ;
74+ int m = ans. length();
75+ for (int j = 0 ; j < m; ) {
76+ int k = j + 1 ;
77+ while (k < m && ans. charAt(j) == ans. charAt(k)) k++ ;
78+ int cnt = k - j;
79+ cur += cnt + " " + ans. charAt(j);
80+ j = k;
9781 }
82+ ans = cur;
9883 }
99- sb. append(cnt);
100- sb. append(c);
101- return sb. toString();
84+ return ans;
10285 }
10386}
10487```
105- * 时间复杂度:循环 ` n ` 次,复杂度为 $O(n)$;每次循环处理一遍当前的 ` s ` 字符串,复杂度为 $O(n)$ 。整体复杂度为 $O(n ^2)$。
88+ * 时间复杂度:$O(n^2)$
10689* 空间复杂度:$O(n)$
10790
10891---
10992
110- ### 使用「哨兵技巧」做一些小优化
111- 112- 之前三叶在不少的链表题中为你介绍过「哨兵技巧」,例如 [ 24. 两两交换链表中的节点(中等)] ( https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247485831&idx=1&sn=9881e540c2c329d211dfb12308a9ce86&chksm=fd9ca298caeb2b8e5120e16462e4b4722cedecdd122d97cf049a2f5f32a0254ca461c660d678&scene=178&cur_album_id=1805384400772087811#rd ) 。
93+ ### 打表
11394
114- ** 但是「哨兵」可不是只能应用在链表题上,「哨兵」的本质是帮助我们简化边界的判断。 **
95+ 利用数据范围只有 30ドル,ドル我们可以使用 ` static ` 进行打表操作,从而节省掉不同样例之间的「重复」部分的计算量。
11596
116- 例如我们上述解法中,就可以使用「哨兵技巧」来简化我们统计的逻辑,避免在循环外统计最后一次字符 。
97+ 例如对于 $n = 5$ 和 $n = 6$ 都存在先计算前五项的公共部分,打表可以确保这部分只会被计算一次,同时能够应用到后面项中 。
11798
11899代码:
119- ``` Java []
100+ ``` Java
120101class Solution {
121- public String countAndSay (int n ) {
122- String s = " 1" ;
123- for (int i = 2 ; i <= n; i++ ) {
124- s = nextString(s);
125- }
126- return s;
127- }
128- String nextString (String s ) {
129- // 往结尾添加一个"哨兵"
130- s = s + " a" ;
131- StringBuilder sb = new StringBuilder ();
132- int n = s. length();
133- char [] cs = s. toCharArray();
134- char c = cs[0 ];
135- int cnt = 1 ;
136- for (int i = 1 ; i < n; i++ ) {
137- char cur = cs[i];
138- if (cur == c) {
139- cnt++ ;
140- } else {
141- sb. append(cnt);
142- sb. append(c);
143- 144- c = cur;
145- cnt = 1 ;
102+ static String [] f = new String [35 ];
103+ static {
104+ f[1 ] = " 1" ;
105+ for (int i = 2 ; i < 35 ; i++ ) {
106+ String prev = f[i - 1 ], cur = " " ;
107+ int m = prev. length();
108+ for (int j = 0 ; j < m; ) {
109+ int k = j + 1 ;
110+ while (k < m && prev. charAt(j) == prev. charAt(k)) k++ ;
111+ int cnt = k - j;
112+ cur += cnt + " " + prev. charAt(j);
113+ j = k;
146114 }
115+ f[i] = cur;
147116 }
148- return sb. toString();
117+ }
118+ public String countAndSay (int n ) {
119+ return f[n];
149120 }
150121}
151122```
152- * 时间复杂度:循环 ` n ` 次 ,复杂度为 $O(n )$;每次循环处理一遍当前的 ` s ` 字符串,复杂度为 $O(n)$ 。整体复杂度为 $O(n^2)$。
153- * 空间复杂度:$O(n )$
123+ * 时间复杂度:将打表逻辑到放本地执行 ,复杂度为 $O(1 )$;放到 $OJ$ 执行则为 $O(n^2)$
124+ * 空间复杂度:$O(C )$
154125
155126---
156127
157128### 最后
158129
159- 这是我们「刷穿 LeetCode」系列文章的第 ` No.38 ` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完 。
130+ 这是我们「刷穿 LeetCode」系列文章的第 ` No.38 ` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完 。
160131
161132在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
162133
0 commit comments