Skip to content

Commit b690296

Browse files
authored
Merge pull request apachecn#53 from yudaer/master
Create 906._Super_Palindromes.md
2 parents 251cfbc + 32ac465 commit b690296

File tree

6 files changed

+540
-0
lines changed

6 files changed

+540
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
### 887. Super Egg Drop
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/super-egg-drop
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 已知蛋从F楼丢下会坏
14+
2. 现在给你K个蛋,在N层楼中,请问最坏情况最少要试验几次才能知道F的精确值
15+
3. 如果蛋丢下来不会坏,那么可以捡起来继续丢
16+
17+
思路:
18+
19+
- 又是一个脑筋急转弯的题目,我们来看看K=1的情况。由于只有一个蛋,坏了就没了,所以只能一层一层往下丢。一层丢下,蛋坏了,说明F=1,反之,继续去二层丢,所以K=1时,最坏情况要试验N次
20+
- 当K>=2时,由于蛋有多余,可以大胆的去中间丢,这样就可以少丢几次
21+
- 因此,令`cache[K][N]`表示K个蛋在N层试验最少要试验的次数,假设第一次要在i层丢蛋,那么分成了两个子问题,如果蛋没碎了,那么最少次数等于`cache[k][N-i]+1`,如果蛋碎了,那么最少次数等于`cache[k-1][i-1]+1`
22+
- 那么这个题就变成了`cache[k][N] = min[1<=i<=N]{max{cache[k][N-i], cache[k - 1][i - 1] + 1} + 1}`,时间复杂度是o(kNN)
23+
- 但是看数据范围,k<=100,N<=10000,o(kNN)肯定是过不了的
24+
- 注意到,`cache[k][N]`是非严格递增输了,且递增值不会超过1,即`0<=cache[k][i+1]-cache[k][i]<=1`
25+
- 计算过程中令`cache[k][N]`最优解的i,跟`cache[k][N+1]`最优解的j,关系是`0<=j-i<=1`
26+
- 因此在处理的过程,固定k,递推N,保存上一个最优解i,继续推出下一个最优解
27+
28+
代码:
29+
30+
```java
31+
class Solution {
32+
private static int[][] cache;
33+
34+
private static void init() {
35+
if (cache == null) {
36+
cache = new int[101][10001];
37+
38+
for (int i = 1;i <= 10000;i++) {
39+
cache[1][i] = i;
40+
}
41+
42+
for (int i = 2;i <= 100;i++) {
43+
cache[i][1] = 0;
44+
cache[i][1] = 1;
45+
cache[i][2] = 2;
46+
47+
int idx = 1;
48+
for (int j = 3;j <= 10000;j++) {
49+
50+
while (cache[i - 1][idx + 1] <= cache[i][j - idx - 2]) {
51+
idx++;
52+
}
53+
54+
cache[i][j] = Math.max(cache[i - 1][idx], cache[i][j - idx - 1]) + 1;
55+
}
56+
}
57+
}
58+
}
59+
60+
public int superEggDrop(int K, int N) {
61+
init();
62+
return cache[K][N];
63+
}
64+
}
65+
```
66+
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
### 895. Maximum Frequency Stack
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/maximum-frequency-stack/
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 一个特殊的类栈数据结构,具有两种操作
14+
2. push x:表示将x入栈
15+
3. pop:表示将栈中最多的数弹出,相同数量的数弹出最近入栈的一个数
16+
17+
思路:
18+
19+
- 这个题的数据结构中,x是key,既需要根据x来操作,又需要根据x的数量和入栈时间来出栈,典型的双向查找表
20+
21+
- 维护一个x到x的入栈时间的映射关系,入栈时间我们定义为第几次push操作,即x->[t1,t2,t3]
22+
23+
- 维护一个**有序**的x的入栈时间到x的对应表,比较函数定义为:栈中元素个数,和栈顶时间
24+
25+
代码:
26+
27+
```java
28+
class FreqStack {
29+
30+
int _count;
31+
private class Item implements Comparable<Item> {
32+
LinkedList<Integer> time;
33+
34+
public Item(LinkedList<Integer> time) {
35+
this.time = time;
36+
}
37+
38+
public LinkedList<Integer> getTime() {
39+
return time;
40+
}
41+
42+
@Override
43+
public int compareTo(Item o) {
44+
if (time.size() == o.time.size()) {
45+
return Integer.compare(o.time.getLast(), time.getLast());
46+
} else {
47+
return Integer.compare(o.time.size(), time.size());
48+
}
49+
}
50+
}
51+
52+
TreeMap<Item, Integer> freqToId;
53+
Map<Integer, Item> idToFreq;
54+
55+
public FreqStack() {
56+
_count = 0;
57+
freqToId = new TreeMap<Item, Integer>();
58+
idToFreq = new HashMap<Integer, Item>();
59+
}
60+
61+
public void push(int x) {
62+
if (!idToFreq.containsKey(x)) {
63+
LinkedList<Integer> time = new LinkedList<Integer>();
64+
time.addLast(_count++);
65+
Item item = new Item(time);
66+
freqToId.put(item, x);
67+
idToFreq.put(x, item);
68+
} else {
69+
Item origin = idToFreq.get(x);
70+
freqToId.remove(origin);
71+
LinkedList<Integer> time = origin.time;
72+
time.addLast(_count++);
73+
Item item = new Item(time);
74+
idToFreq.put(x, item);
75+
freqToId.put(item, x);
76+
}
77+
}
78+
79+
public int pop() {
80+
Map.Entry<Item, Integer> first = freqToId.firstEntry();
81+
freqToId.remove(first.getKey());
82+
idToFreq.remove(first.getValue());
83+
84+
LinkedList<Integer> time = first.getKey().time;
85+
time.removeLast();
86+
if (time.size() != 0) {
87+
Item item = new Item(time);
88+
idToFreq.put(first.getValue(), item);
89+
freqToId.put(item, first.getValue());
90+
}
91+
92+
return first.getValue();
93+
}
94+
}
95+
```
96+
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
### 899. Orderly Queue
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/orderly-queue/
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 给定一个字符串S和一个数K
14+
2. 每一轮可以选择字符串的前K个数的其中一个,把它移到放在最后面
15+
3. 无限次数后,问生成的最小字符串是什么
16+
17+
思路:
18+
19+
- 不要考虑暴力了,由于是无限次数,连暴力搜索都无从下手,除非出动启发式搜索
20+
- 这是个脑筋急转弯的题目,分为两种情况
21+
- 当K=1时,由于每次只能取第一个放在最后面,这种情况是暴力搜索,时间复杂度是o(nk),k是字符串的长度
22+
- 当K>=2时,我们拿K=2出来考虑,证明当K=2时,可以生成任一字符串。
23+
- 固定第一个数,不断的把第二个数放在最后面,这种做法可以使第一个数插入到队列的任一位置
24+
- 假设队列A1-Ak有序,我们要把第A(k+1)插入到Ak的后面,只需要把A(k+1)固定在第一个数,重复第一个做法即可
25+
- 根据数据归纳法,当K=2时,可以生成任一字符串
26+
- 因此当K>=2时,只需要排序字符串里面的字符即可
27+
28+
拓展:
29+
30+
- 这道题有o(nlogn)的解法,当k=1时,可以用o(nlogn)的排序算法,找到最小的字符串。有兴趣的同学可以百度一下“后缀数组”,一百行代码左右,这里就不展示了
31+
32+
代码:
33+
34+
```java
35+
class Solution {
36+
public String orderlyQueue(String S, int K) {
37+
if (K == 1) {
38+
String min = S;
39+
for (int i = 0;i < S.length();i++) {
40+
S = S.substring(1, S.length()) + S.charAt(0);
41+
if (min.compareTo(S) > 0) {
42+
min = S;
43+
}
44+
}
45+
return min;
46+
} else {
47+
char[] c = S.toCharArray();
48+
Arrays.sort(c);
49+
return new String(c);
50+
}
51+
}
52+
}
53+
```
54+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
### 902. Numbers At Most N Given Digit Set
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/numbers-at-most-n-given-digit-set/
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 给定一个1-9的集合S,和一个上限k
14+
2. 求这个集合中能构成多少个数,并且不大于k
15+
16+
思路:
17+
18+
- 分情况计算
19+
- 当构成的数的位数小于k的位数,那么能构成的数数量为|S|^位数
20+
- 当构成的数的位数等于k的位数,遍历集合S的数a,判断a
21+
- 如果小于k的最高位,那么能够成的数数量为|S|^(k的位数-1)
22+
- 如果等于k的最高位,继续判断下一位
23+
- 如果不存在等于k的最高位的数,不判断下一位,直接跳出循环
24+
25+
代码:
26+
27+
```java
28+
class Solution {
29+
private int[] split(int a) {
30+
int[] ret = new int[10];
31+
int idx = 0;
32+
while (a != 0) {
33+
ret[idx++] = a % 10;
34+
a /= 10;
35+
}
36+
return Arrays.copyOf(ret, idx);
37+
}
38+
39+
public int atMostNGivenDigitSet(String[] D, int N) {
40+
int[] d = new int[D.length];
41+
for (int i = 0;i < D.length;i++) {
42+
d[i] = Integer.parseInt(D[i]);
43+
}
44+
45+
int[] length = new int[10];
46+
length[0] = 1;
47+
for (int i = 1;i < 10;i++) {
48+
length[i] = length[i - 1] * D.length;
49+
}
50+
51+
int[] a = split(N);
52+
int ret = 0;
53+
for (int i = 1;i < a.length;i++) {
54+
ret += length[i];
55+
}
56+
int i;
57+
for (i = a.length - 1;i >= 0;i--) {
58+
boolean find = false;
59+
for (int j = 0;j < d.length;j++) {
60+
if (a[i] == d[j]) {
61+
find = true;
62+
}
63+
if (d[j] < a[i]) {
64+
ret += length[i];
65+
}
66+
}
67+
if (!find) {
68+
break;
69+
}
70+
}
71+
if (i < 0) {
72+
ret++;
73+
}
74+
return ret;
75+
}
76+
}
77+
```
78+
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
### 903. Valid Permutations for DI Sequence
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/valid-permutations-for-di-sequence
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 给定一个字符串,长度为n,D代表前一个数比下一个数大而I代表前一个数比下一个数小
14+
2. 求出在所有0-n的排列中,满足上面字符串所代表的排列两两之间大小比较的排列的个数
15+
16+
思路:
17+
18+
- 最大的那个数,可能出现在下面三种情况:
19+
- 出现在位置0,a[0]>a[1]
20+
- 出现在位置i,a[i]>a[i+1]且a[i]<a[i-1]
21+
- 出现在位置n,a[n]>a[n-1]
22+
- 枚举最大的数出现的位置,假设为i,当i选定为最大的数后,剩下的数要分配给左右两边,有c(n, i)种情况,并且左右两边的队列互相独立
23+
- 这时候左右两边分别成了一个命题相同的子问题,求出即可
24+
- 注意:需要注意的是,不是所有的动态规划都需要用递推的方式求解。这道题可以选择另一种写法,就是递归+缓存,代码量会少不少
25+
26+
代码:
27+
28+
```java
29+
class Solution {
30+
private static int[][] c;
31+
private static int MOD = 1000000007;
32+
private static int[][] cache = new int[202][202];
33+
34+
private void init() {
35+
c = new int[202][202];
36+
c[0][0] = 1;
37+
for (int i = 1;i <= 201;i++) {
38+
c[i][0] = 1;
39+
for (int j = 1;j < i;j++) {
40+
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
41+
if (c[i][j] >= MOD) {
42+
c[i][j] -= MOD;
43+
}
44+
}
45+
c[i][i] = 1;
46+
}
47+
}
48+
49+
public int find(String S, int left, int right) {
50+
int ret = 0;
51+
if (right - left == 0) {
52+
return 1;
53+
}
54+
if (cache[left][right] != -1) {
55+
return cache[left][right];
56+
}
57+
if (S.charAt(left) == 'D') {
58+
ret += find(S, left + 1, right);
59+
if (ret >= MOD) {
60+
ret -= MOD;
61+
}
62+
}
63+
if (S.charAt(right - 1) == 'I') {
64+
ret += find(S, left, right - 1);
65+
if (ret >= MOD) {
66+
ret -= MOD;
67+
}
68+
}
69+
for (int i = left + 1;i < right;i++) {
70+
if (S.charAt(i) == 'D' && S.charAt(i - 1) == 'I') {
71+
ret += ((long) find(S, left, i - 1) * find(S, i + 1, right) % MOD) * c[right - left][i - left] % MOD;
72+
if (ret >= MOD) {
73+
ret -= MOD;
74+
}
75+
}
76+
}
77+
if (ret == 0) {
78+
return cache[left][right] = 1;
79+
}
80+
return cache[left][right] = ret;
81+
}
82+
83+
public int numPermsDISequence(String S) {
84+
init();
85+
for (int i = 0;i < 202;i++) {
86+
for (int j = 0;j < 202;j++) {
87+
cache[i][j] = -1;
88+
}
89+
}
90+
int ret = find(S, 0, S.length());
91+
return ret;
92+
}
93+
}
94+
```
95+

0 commit comments

Comments
 (0)