总体来说,这周题目比较难。以后还是每周都来总结下周赛。
1. 957. Prison Cells After N Days
题意
类似于细胞生命的游戏,每天细胞会根据前一天的状态进行演变,演变规则:
Each day, whether the cell is occupied or vacant changes according to the
following rules:
If a cell has two adjacent neighbors that are both occupied or both vacant,
then the cell becomes occupied.
Otherwise, it becomes vacant.
思路
开始比较直接的做法就是遍历n天,再遍历n个细胞:
public int[] prisonAfterNDays(int[] cells, int N) {
while (N > 0) {
N--;
int[] cells2 = new int[8];
for (int i = 1; i < 7; ++i)
cells2[i] = cells[i - 1] == cells[i + 1] ? 1 : 0;
cells = cells2;
}
return cells;
}
但是这个解答在N很大时超时,于是必须考虑优化方案了。
- 我们来思考一下,这个状态中可能出现重复的吗?也就是如果存在循环,就会好办很多,我们就可以取周期的余,就到了跳过一次循环后的状态了;其实一共8个细胞,出去首尾两个状态始终是0,其余6个组成的状态数一共是
2^6=64
种,所以变化那么多天,肯定是存在循环的(其实就是也可以猜测如果没有循环这题就没法解了= =) - 下一步,怎么判断出现过的状态呢?并且还需要知道是在之前哪天出现的,因为今天减去上一期相同状态出现的那天,就等于循环的周期啦。自然而然想到应该用hash table存储。
- 还需要注意的点就是,hash table中的key的存储,应该是用字符串化后的数组,不然hash table判断的时候用的是数组的reference,每次都不会一样.
代码:
class Solution {
public int[] prisonAfterNDays(int[] cells, int N) {
int[] tmp = new int[8];
Map<String, Integer> map = new HashMap<>();
while (N > 0) {
map.put(Arrays.toString(cells), N);
N--;
for (int i = 1; i < 7; i++) {
tmp[i] = cells[i - 1] == cells[i + 1] ? 1 : 0;
}
for (int i = 0; i < 8; i++) cells[i] = tmp[i];
if (map.containsKey(Arrays.toString(cells))) {
int T = map.get(Arrays.toString(cells)) - N; //周期
N %= T;
}
}
return cells;
}
}
总结:
这种状态转换,且n还很大,一般就是需要找循环,然后取余mod,来减少循环长度。
这样的就遍历数组,再加上一个优化步骤的题目,面试也会将常考
2. 958. Check Completeness of a Binary Tree
题意
判断一棵树是否是完全二叉树
思路
根据完全二叉树的性质,最下层节点应该都在左边;
- 可以考虑下层序遍历,如果是完全二叉树的话,那么当遇到空节点的时候,中后就不应该再遇到空节点了。
- 层序遍历,无论是否为空节点都add进队列,当队列头是空节点时,说明遇到第一个空节点了;然后再判断队列中剩下的节点,如果有非空的,则不是完全二叉树
代码:
class Solution {
public boolean isCompleteTree(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (queue.peek() != null) {
TreeNode cur = queue.poll();
queue.add(cur.left);
queue.add(cur.right);
}
while (!queue.isEmpty() && queue.peek() == null) {
queue.poll();
}
return queue.isEmpty();
}
}
总结
树的层序遍历的扩展
3. 959. Regions Cut By Slashes
哈哈比赛时这题题目都没看懂 = =
题意
In a N x N grid composed of 1 x 1 squares, each 1 x 1 square consists of a /
, or blank space. These characters divide the square into contiguous
regions.
大概就是给一个字符串代表矩形的分割方式,求在矩形区域的个数。
思路
什么鬼题目??大脑一片空白?莫急,让我们慢慢分析一下。
- 怎么才能判断这块区域是一块呢?这种图形题,又不可能考计算几何 = =,所以唯一可能就是通过染色的方法,我们可以标记对吧;
- 那么怎么染色呢?是不是想到了并查集?就是将相连的区域都染为同一个颜色,最后颜色的树木不就是的区域的数目吗?
- 有了基本的思路,方法是可行的,到了具体实现了;这里一个难想到的点就是,根据
、
/
这两个字符,去分割矩形小框框,怎么去分呢?这里需要把每个小矩形再分成四个小区域:如下图所示:
这样,我们就可以根据当前是什么符号,去将不同的区域进行连接了
代码:
class Solution {
class UF {
int[] parent;
public UF(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
public int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
}
public void union(int x, int y) {
parent[find(x)] = find(y);
}
}
int count = 0;
UF uf;
public int regionsBySlashes(String[] grid) {
int n = grid.length;
uf = new UF(n * n * 4);
count = n * n * 4;
//四个小区块:top: 0, right: 1, bottom: 2, left: 3
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (r > 0) {
check(changeIndex(n, r-1, c, 2), changeIndex(n, r, c, 0));
}
if (c > 0) {
check(changeIndex(n, r, c - 1, 1), changeIndex(n, r, c, 3));
}
if (grid[r].charAt(c) != '/') {
check(changeIndex(n, r, c, 3), changeIndex(n, r, c, 2));
check(changeIndex(n, r, c, 1), changeIndex(n, r, c, 0));
}
if (grid[r].charAt(c) != '\') {
check(changeIndex(n, r, c, 0), changeIndex(n, r, c, 3));
check(changeIndex(n, r, c, 1), changeIndex(n, r, c, 2));
}
}
}
return count;
}
private int changeIndex(int n, int x, int y, int k) {
return (x * n + y) * 4 + k;
}
private void check(int n1, int n2) {
if (uf.find(n1) != uf.find(n2)) {
uf.union(n1, n2);
count--;
}
}
}
总结
并查集的考察越来多多了
4. 960. Delete Columns to Make Sorted III
题意
这题是之前几题的延续
删除某些列后,每行的元素都是按字母排序的
Suppose we chose a set of deletion indices D such that after deletions, the
final array has every element (row) in lexicographic order.
思路
- 求每个字符串最少删掉的字符,使得每个字符串中的字符都是递增的;我们可以先考虑考虑1个字符串的情况:
也就是说,比如"edcba"
,求它最少减少的字符,使得它自增,是不是觉得很熟悉呢?问题可以转化为最长递增子序列LIS,n - LIS
即为所求了!回忆LIS的基本解法是:
for j : 0 -> n
for i : 0 -> j
if (A[i] < A[j]) dp[j] = max(dp[j], dp[i] + 1)
- 想清楚了一个字符串的case,再来想想多个字符串怎么办?
这里的一个思路就是 将每列看成一个元素,那么只要判断在每个case下,是不是每行元素都满足递增,如果满足,则更新dp;否则跳过;
代码:
class Solution {
public int minDeletionSize(String[] A) {
int m = A.length, n = A[0].length();
int[] dp = new int[n]; //每列看成一个元素!
int res = Integer.MAX_VALUE;
Arrays.fill(dp, 1);
for (int j = 0; j < n; j++) {
for (int i = 0; i < j; i++) {
boolean valid = true;
//检查每个字符串是不是都满足
for (int r = 0; r < m; r++) {
if (A[r].charAt(i) > A[r].charAt(j)) {
valid = false;
break;
}
}
if (valid) dp[j] = Math.max(dp[j], dp[i] + 1);
}
res = Math.min(res, n - dp[j]);
}
return res;
}
}
总结
其实这题就是LIS的变种
总体来说,这次周赛除了第三题,其他都可以做出来的,其他的都比较常规,但是...emmm 加油吧