Leetcode刷题 2021.01.25
Leetcode481 神奇字符串
神奇的字符串 S 只包含 ‘1’ 和 ‘2’,并遵守以下规则:
字符串 S 是神奇的,因为串联字符 ‘1’ 和 ‘2’ 的连续出现次数会生成字符串 S 本身。
字符串 S 的前几个元素如下:S = “1221121221221121122 …”
如果我们将 S 中连续的 1 和 2 进行分组,它将变成:
1 22 11 2 1 22 1 22 11 2 11 22 …
并且每个组中 ‘1’ 或 ‘2’ 的出现次数分别是:
1 2 2 1 1 2 1 2 2 1 2 2 …
你可以看到上面的出现次数就是 S 本身。
给定一个整数 N 作为输入,返回神奇字符串 S 中前 N 个数字中的 ‘1’ 的数目。
注意:N 不会超过 100,000。
没什么特别的技巧,就是模拟这个写法而已。注意一开始先设定前三个数,因为后面的数会越来越多,不会造成不知道后面是什么数的情况乱。这句话可能读不懂,但是明白题目意思的读者应该能理解
class Solution {
public int magicalString(int n) {
if (n == 0) return 0;
if (n <= 3) return 1;
//设置一个数组,设定数组的前三个数
int[] arr = new int[n];
arr[0] = 1;
arr[1] = 2;
arr[2] = 2;
int i = 2, j = 3, res = 1;
int level = 1;
while (j < n){
int count = arr[i];
//双指针模拟,如果为1就res++
for(int k = 0; k < count; k++){
if (j + k >= n) break;
if (level % 2 == 0){
arr[j + k] = 2;
}else{
arr[j + k] = 1;
res++;
}
}
level++;
j = j + count;
i++;
}
return res;
}
}
Leetcode443 压缩字符串
给定一组字符,使用原地算法将其压缩。
压缩后的长度必须始终小于或等于原数组长度。
数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。
在完成原地修改输入数组后,返回数组的新长度。
这类双指针题其实本质都不难,关键是一些边界的地方,有时候会整不明白,应该多调试几遍就差不多了。思路还是比较清晰的,因为要原地修改,就双指针获取压缩字符的长度。再根据一些条件进行判断就行了。
class Solution {
public int compress(char[] chars) {
int i = 0, j = 0, n = chars.length, res = 0, index = 0;
//双指针常规写法
while (j < n){
while (j < n && chars[j] == chars[i]){
j++;
}
//获取字符的长度
int count = j - i;
//把第一个改成该字符
chars[index] = chars[i];
//获取长度的位数
int len = getNumLength(count);
//为1的话就不需要后面加数字了
if (count != 1){
//根据位数填入相应的数字
for(int k = index + len; k > index; k--){
if (count == 0) break;
chars[k] = (char) (count % 10 + '0');
count /= 10;
}
index += len + 1;
}else{
index++;
}
//更新i
i = j;
}
return index;
}
private int getNumLength(int x){
int res = 0;
while (x != 0){
res++;
x /= 10;
}
return res;
}
}
Leetcode959 由斜杠划分区域
在由 1 x 1 方格组成的 N x N 网格 grid 中,每个 1 x 1 方块由 /、\ 或空格构成。这些字符会将方块划分为一些共边的区域。
(请注意,反斜杠字符是转义的,因此 \ 用 “\” 表示。)。
返回区域的数目。
今天的每日一题,又是并查集。虽然是并查集但是却毫无头绪,并查集难的地方就在于不知道怎么去合并,感觉有各种的奇淫巧计。这道题的话也是如此,参考了官方代码。
class Solution {
public int regionsBySlashes(String[] grid) {
int n = grid.length;
//每一个小格都分成四个小小格
int size = 4 * n * n;
UnionFind uf = new UnionFind(size);
for(int i = 0; i < n; i++){
char[] arr = grid[i].toCharArray();
for(int j = 0; j < n; j++){
//获取每一个小小格的索引
int index = 4 * (i * n + j);
char c = arr[j];
//如果是'/'的话,合并0,3和 1,2
if (c == '/'){
uf.union(index, index + 3);
uf.union(index + 1, index + 2);
//如果是'\\'合并0,1和2,3
}else if (c =='\\'){
uf.union(index, index + 1);
uf.union(index + 2, index + 3);
//否则四个都合并
}else{
uf.union(index, index + 1);
uf.union(index + 1, index + 2);
uf.union(index + 2, index + 3);
}
//向下向右进行合并
if (j + 1 < n){
uf.union(index + 1, 4 * (i * n + j + 1) + 3);
}
if (i + 1 < n){
uf.union(index + 2, 4 * ((i + 1) * n + j));
}
}
}
//最后获取联通分量个数就行,可以在合并过程中维护,也可以最后再算一次。
return uf.getCount();
}
class UnionFind{
int[] parent;
int count;
public UnionFind(int n){
this.count = n;
this.parent = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
public int getCount(){
return count;
}
public int find(int i){
if (parent[i] == i){
return i;
}
return parent[i] = find(parent[i]);
}
public void union(int x, int y){
int root1 = find(x);
int root2 = find(y);
if (root1 == root2) return;
parent[root1] = root2;
count--;
}