933. Number of Recent Calls
https://leetcode.com/problems/number-of-recent-calls/description/
这道题,维护一个队列,每当一个新的时间进来,看看队列头,时间是不是超出了那个范围,超出了就POP,一直到在范围里。
Deque q = new ArrayDeque<>();
public RecentCounter() {
}
public int ping(int t) {
while(!q.isEmpty() && t - q.peekFirst() > 3000) {
q.pollFirst();
}
q.offerLast(t);
return q.size();
}
934. Shortest Bridge
这道题用FLOOD FILL 找到2个岛,分别把坐标存进2个SET。
随后就可以用BFS,找最短路的。这里BFS,和word ladder那道题是一样的。
对所有点,往外扩散,找到这个岛所有一步可达,所有2步可达,所有三步可达。一直到触碰到另外一个岛。
int h;
int l;
final int[][] dirs = {{1,0},{0,1},{0,-1},{-1,0}};
public int shortestBridge(int[][] A) {
h = A.length;
l = A[0].length;
Set st = new HashSet<>();
Set ed = new HashSet<>();
for (int i = 0; i < h; i++) {
for (int j = 0; j < l; j++) {
if (A[i][j] == 0) continue;
if(st.isEmpty())
floodfill(st,A,i,j);
else
floodfill(ed,A,i,j);
}
}
int step = 0;
Set seen = new HashSet<>(st);
while(true) {
Set next = new HashSet<>();
for (int i : st) {
int y = i / l;
int x = i % l;
for(int[] dir : dirs){
int ny = y + dir[0];
int nx = x + dir[1];
int nkey = ny * l + nx;
if(ny == h || nx == l || nx < 0 || ny < 0 || seen.contains(nkey)) continue;
if(ed.contains(nkey)) return step;
seen.add(nkey);
next.add(nkey);
}
}
st = next;
step++;
}
}
private void floodfill(Set s, int[][] A, int y, int x) {
s.add(y * l + x);
for(int[] dir : dirs){
int ny = y + dir[0];
int nx = x + dir[1];
int nkey = ny * l + nx;
if(ny == h || nx == l || nx < 0 || ny < 0 || A[ny][nx] == 0 || s.contains(nkey)) continue;
floodfill(s,A,ny,nx);
}
}
935. Knight Dialer
https://leetcode.com/problems/knight-dialer/description/
这道题我看了下,就是每个状态可以转移到另外的一些状态。
根据跳跃规则。那么一共有10个状态。每个状态可以通过一步跳到对应的状态我存进STEP。
随后就是对每一步的这个状态,去映射到下一步的状态。用个DP数组来存。
最后对这种步长的所有走法求和就行。
dp i j 就是 花了 I 步 落在第J个数字上的步数。
那么转移方程,就是根据一个数字 怎么到 另一个数字 都加上去。
public int knightDialer(int N) {
int[][] dp = new int[N][10];
int[][] steps = new int[][]{{4,6},{8,6},{7,9},{4,8},{3,9,0},{},{1,7,0},{2,6},{1,3},{2,4}};
int M = 1000000007;
Arrays.fill(dp[0],1);
for (int i = 1; i < N; i++) {
for (int j = 0; j < 10; j++) {
for(int k = 0; k < steps[j].length; k++){
dp[i][steps[j][k]] = (dp[i][steps[j][k]] + dp[i-1][j])%M;
}
}
}
long res = 0;
for (int i = 0; i < 10; i++) {
res += dp[N-1][i];
}
res %= M;
return (int)res;
}
936. Stamping The Sequence
https://leetcode.com/problems/stamping-the-sequence/description/
这道题的难点在于逆向思维,以终·为始。如果我们可以一开始就构造好了,随后要把他们全抹掉,记录顺序,顺序也是反着生成的。
首先我们定义每一个位置,都有2种属性,可以直接消的就是字符相等的集合,和没法在这个位置上消的字符。
分别放进MADE, TODO。
然后我们把所有MADE 是满的,也就是TODO 是空的,那些MADE的坐标放进QUEUE。 这个点,都是最后一步覆盖上去的。
然后把这个点,都放进STACK。随后从QUEUE里弹出来用过的坐标。对他们能影响到的其他NODE里,去删除那些NODE里的TODO。 那么一旦有新的NODE,TODO 是空了,又可以放进QUEUE。
最后根据stack 就可以反向生成步骤了。
public int[] movesToStamp(String stamp, String target) {
int sl = stamp.length();
int tl = target.length();
Queue q = new LinkedList<>();
boolean[] done = new boolean[tl];
Stack st = new Stack<>();
List res = new ArrayList<>();
for (int i = 0; i <= tl - sl; i++) {
Set made = new HashSet<>();
Set todo = new HashSet<>();
for (int j = 0; j < sl; j++) {
if (stamp.charAt(j) == target.charAt(i + j)) {
made.add(i + j);
} else {
todo.add(i + j);
}
}
Node cur = new Node(made, todo);
if (todo.isEmpty()) {
for (int j : made) {
if (done[j]) continue;
q.offer(j);
done[j] = true;
}
st.push(i);
}
res.add(cur);
}
while (!q.isEmpty()) {
int cur = q.poll();
for (int i = Math.max(0,cur - sl + 1); i < Math.min(tl - sl + 1,cur + sl); i++) {
Node newn = res.get(i);
if (!newn.todo.contains(cur)) continue;
newn.todo.remove(cur);
if (newn.todo.isEmpty()) {
for (int j : newn.made) {
if (done[j]) continue;
q.offer(j);
done[j] = true;
}
st.push(i);
}
}
}
for(boolean d : done) if(!d) return new int[0];
int[] ans = new int[st.size()];
int idx = 0;
while (!st.isEmpty()) {
ans[idx++] = st.pop();
}
return ans;
}
class Node {
Set made, todo;
public Node(Set made, Set todo) {
this.made = made;
this.todo = todo;
}
}