owenzhang的博客

Leetcode 第139场周赛解题报告

字数统计: 1.8k阅读时长: 8 min
2019/04/21
loading

今天的比赛考固定算法的不多,只有最后一道动态规划的题目稍难一点。其他的题目花些心思,也可以想出来。在面试中如果考察一个人的脑力,前三道题目比较好。都能答出来一些,但是也有优化的空间。

第三题用了很多时间,导致第四题时间都不够了。做比赛和工程还是有差别,应该有思路就敲代码,先把题目过了再说,一些细节不用考虑太多。有些地方不优化也可以过掉。但是在工程上就要考究些,因为代码是在服务器24小时跑,能优化一点点,在海量服务中也能节省很多计算资源。

下面是详细的题解和思考。


比赛的地址 Weekly Contest 139

https://leetcode-cn.com/contest/weekly-contest-139

1. 字符串的最大公因子

题目:

字符串的最大公因子(Greatest Common Divisor of Strings)

地址:

https://leetcode-cn.com/contest/weekly-contest-139/problems/greatest-common-divisor-of-strings/

题意:

对于字符串 S 和 T,只有在 S = T + … + T(T 与自身连接 1 次或多次)时,我们才认定 “T 能除尽 S”。

返回字符串 X,要求满足 X 能除尽 str1 且 X 能除尽 str2。

思路:

返回的公共字符串X就是经过n1次重复为Str1,经过n2次重复为Str2的最长字符串。

那么X一定是S和T的公共前缀字符串的子串。

先计算出S和T的公共前缀字符串str,然后从str的最长前缀到最短前缀依次判断,是否满足能「除尽」str1和str2。

要求的X的字符串长度,一定是str1和str2长度的公约数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Solution {
public:
bool isSubStr(string & str1, string & str)
{
int len1 = str1.size();
int len2 = str.size();
if(len1%len2!=0)
return false;
int repeat = len1/len2;
string tmp;
while(repeat--)
{
tmp += str;
}
return tmp == str1;
}
string gcdOfStrings(string str1, string str2) {
string str;
int len1 = str1.size();
int len2 = str2.size();
for(int i = 0; i < len1 && i < len2; ++i)
{
if(str1[i] == str2[i])
{
str.push_back(str1[i]);
}
else
break;
}
int len = str.size();
while(len > 0)
{

bool b1 = isSubStr(str1, str);
bool b2 = isSubStr(str2, str);
if(b1 && b2)
return str;
str.pop_back();
len --;
}
return str;
}
};

2. 按列翻转得到最大值等行数

题目:

按列翻转得到最大值等行数(Flip Columns For Maximum Number of Equal Rows)

地址:

https://leetcode-cn.com/contest/weekly-contest-139/problems/flip-columns-for-maximum-number-of-equal-rows/

题意:

给定由若干 0 和 1 组成的矩阵 matrix,从中选出任意数量的列并翻转其上的 每个 单元格。翻转后,单元格的值从 0 变成 1,或者从 1 变为 0 。

返回经过一些翻转后,行上所有值都相等的最大行数。

思路:

两行数字,经过对一些列的反转,最后都达到这一行的值相同。那么这两行数字的关系有两种情况:

  1. 两行数字的值都相同;
  2. 两行数字的值都相反;

其他任何情况,一定有一列始终都不满足相等。

所以题目的结果为看满足两种情况的行的集合最多有多少?

只要两两枚举就能算出来,曾经加入过其他集合的可以后面就不比较了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public:
int maxEqualRowsAfterFlips(vector<vector<int>>& a) {
int m = a.size();
int n = a[0].size();
int ret = 0;
for(int i = 0; i < m ; ++i)
{
int tmp = 1;
for(int j = i+1; j < m; ++j)
{
if(a[i]==a[j])
{
tmp++;
}
else
{
int k;
for(k = 0; k < n; ++k)
{
if(a[i][k]+a[j][k]!=1)
break;
}
if(k==n)
tmp++;
}
}
ret = max(ret, tmp);
}
return ret;
}
};

3. 负二进制数相加

题目:

负二进制数相加(Adding Two Negabinary Numbers)

地址:

https://leetcode-cn.com/contest/weekly-contest-139/problems/adding-two-negabinary-numbers/

题意:

给出基数为 -2 的两个数 arr1 和 arr2,返回两数相加的结果。

数字以 数组形式 给出:数组由若干 0 和 1 组成,按最高有效位到最低有效位的顺序排列。例如,arr = [1,1,0,1] 表示数字 (-2)^3 + (-2)^2 + (-2)^0 = -3。数组形式 的数字也同样不含前导零:以 arr 为例,这意味着要么 arr == [0],要么 arr[0] == 1。

返回相同表示形式的 arr1 和 arr2 相加的结果。两数的表示形式为:不含前导零、由若干 0 和 1 组成的数组。

思路:

-2进制的算法和2进制算法是一样的。都是先模-2得到当前位的值,然后再除-2得到高位的进位值。

但是由于题目中要求每位必须是0或者是1,所以在模-2等于-1时,要进行特殊处理。

如果取模的结果是-1表示当前位的值为-1,要变成正数才能正常表示。要把当前位变为1,要把除数加1。

如果想不明白,请思考下公式 (x*-2)+(-1) = (x-1)*-2 + 1

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution {
public:
vector<int> addNegabinary(vector<int>& arr1, vector<int>& arr2) {
vector<int> res;
int i = arr1.size()-1;
int j = arr2.size()-1;
int carry = 0;
int divid = -2;
while(i >= 0||j >= 0||carry)
{
if(i>=0)
carry += arr1[i];
if(j>=0)
carry += arr2[j];

int mod = carry % divid;
carry /= divid;

if(mod < 0)
{
mod -= divid;
carry += 1;
}
res.push_back(mod);

--i;
--j;
}
while(res.size()>1)
{
if(res.back()==0)
res.pop_back();
else
break;
}

reverse(res.begin(), res.end());
return res;

}
};

4. 元素和为目标值的子矩阵数量

题目:

元素和为目标值的子矩阵数量(Number of Submatrices That Sum to Target)

地址:

https://leetcode-cn.com/contest/weekly-contest-139/problems/number-of-submatrices-that-sum-to-target/

题意:

给出矩阵 matrix 和目标值 target,返回元素总和等于目标值的非空子矩阵的数量。

子矩阵 x1, y1, x2, y2 是满足 x1 <= x <= x2 且 y1 <= y <= y2 的所有单元 matrix[x][y] 的集合。

如果 (x1, y1, x2, y2) 和 (x1’, y1’, x2’, y2’) 两个子矩阵中部分坐标不同(如:x1 != x1’),那么这两个子矩阵也不同。

思路:

先对每行数据进行处理,求出每行开始到当前位置的和。sum[i][j] = matrix[i][0]+matrix[i][0]+...+matrix[i][j]

这样一行中j,k两个之间的区间和可以表示为sum[i][j]-sum[i][k-1],很快能求出。

之后枚举两列c1,c2,对于每一次枚举,行数从上到下扫描。

每扫描到一行r1,先计算出当前行与两列,还有第0行形成的矩阵的和S。

然后查找是否有之前的行r0的和为S-target。

如果有,则r0,r1,c1,c2形成的和为target。

最后再把S存起来,留给后面的行扫描时查找。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public:
unordered_map<int, int> mp;
int numSubmatrixSumTarget(vector<vector<int>>& matrix, int target) {
int m = matrix.size();
int n = matrix[0].size();

for(int i = 0; i < m; ++i)
{
for(int j = 1; j < n; ++j)
{
matrix[i][j] += matrix[i][j-1];
}
}
int ans = 0;
for(int j = 0; j < n; ++j)
{
for(int k = j; k < n; ++k)
{
mp.clear();
mp[0] = 1;
int sum = 0;
for(int i = 0; i < m; ++i)
{
if(j > 0)
sum += matrix[i][k] - matrix[i][j-1];
else
sum += matrix[i][k];

if(mp.find(sum-target)!=mp.end())
ans += mp[sum-target];

mp[sum]++;

}
}
}
return ans;
}
};
CATALOG
  1. 1. 1. 字符串的最大公因子
  2. 2. 2. 按列翻转得到最大值等行数
  3. 3. 3. 负二进制数相加
  4. 4. 4. 元素和为目标值的子矩阵数量