Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 8bcf9df

Browse files
✨update: Modify 38
1 parent 124b6a7 commit 8bcf9df

File tree

1 file changed

+38
-67
lines changed

1 file changed

+38
-67
lines changed

‎LeetCode/31-40/38. 外观数列(简单).md‎

Lines changed: 38 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7369
class 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
120101
class 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

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /