周赛地址:https://leetcode-cn.com/contest/weekly-contest-234/
第一题:字符串中不同整数的数目
用a-z的字符,将原字符串进行分割,拿到一个只包含数字的字符串数组,对每个字符串,使用自定义的removeZero()函数去除前导0,放入set,最后统计set中元素的个数即可。
比赛的时候wa了两发,不应该啊,没仔细看题,一次wa在前导0上,一次wa在数据范围上。
import java.util.HashSet;
class Solution {
public int numDifferentIntegers(String word) {
String[] words = word.split("[a-z]");
HashSet<String> hashSet = new HashSet<>();
for (String s : words) {
if (!"".equals(s)) {
hashSet.add(removeZero(s));
}
}
return hashSet.size();
}
private String removeZero(String s) {
int i = 0, length = s.length();
while (i < length && s.charAt(i) == '0') {
i++;
}
return s.substring(i);
}
}
第二题:还原排列的最少操作步数
根据题意,对数组模拟运算即可,每次操作后,判断是否回到了初始排列。
class Solution {
public int reinitializePermutation(int n) {
int[] perm = new int[n];
for (int i = 0; i < n; i++) {
perm[i] = i;
}
int times = 0;
int[] arr = perm.clone();
while (true) {
times++;
for (int i = 0; i < n; i++) {
if ((i & 1) == 1) {
arr[i] = perm[n / 2 + (i - 1) / 2];
} else {
arr[i] = perm[i / 2];
}
}
perm = arr.clone();
if (check(perm, n)) {
break;
}
}
return times;
}
private boolean check(int[] arr, int n) {
for (int i = 0; i < n; i++) {
if (arr[i] != i) {
return false;
}
}
return true;
}
}
其实还有更好的方法,只需要看某一位即可,没必要每位都模拟。根据例子可以看出0和length - 1下标的数值是不变的,其余位置的数字回到当前位置所需要的操作数即为答案。
class Solution {
public int reinitializePermutation(int n) {
int times = 0, position = 1;
do {
times++;
if ((position & 1) == 1) {
position = n / 2 + (position - 1) / 2;
} else {
position /= 2;
}
} while (position != 1);
return times;
}
}
第三题:替换字符串中的括号内容
直接模拟就可以了。先把knowledge里的数据放到HashMap里,后续可以直接O(1)根据key获取到value进行替换。
遍历字符串,碰到左括号记录一下下标,碰到右括号记录一下下标,取出(left, right]范围的字符串,去HashMap里查找value,如果能查找到,做替换,查找不到,替换成"?",拼接到结果里。
碰到字符,看下这个字符在不在左右括号里面,如果不在,就直接拼接到结果里。
import java.util.HashMap;
import java.util.List;
class Solution {
public String evaluate(String s, List<List<String>> knowledge) {
HashMap<String, String> hashMap = new HashMap<>();
for (List<String> list : knowledge) {
hashMap.put(list.get(0), list.get(1));
}
StringBuilder stringBuilder = new StringBuilder();
int length = s.length(), left = -1, right = -1;
String key, value;
boolean in = false;
for (int i = 0; i < length; i++) {
if (s.charAt(i) == '(') {
left = i + 1;
in = true;
} else if (s.charAt(i) == ')') {
right = i;
key = s.substring(left, right);
value = hashMap.get(key);
if (value == null) {
value = "?";
}
stringBuilder.append(value);
in = false;
} else {
if (!in) {
stringBuilder.append(s.charAt(i));
}
}
}
return stringBuilder.toString();
}
}
第四题:好因子的最大数目
根据题意,可以知道n满足:
n
=
a
1
b
1
×
a
2
b
2
×
…
…
×
a
k
b
k
n=a_{1}^{b_{1}} \times a_{2}^{b_{2}} \times …… \times a_{k}^{b_{k}}
n=a1b1×a2b2×……×akbk,其中
a
1
,
a
2
,
…
…
,
a
k
a_{1},a_{2},……,a_{k}
a1,a2,……,ak都是n的质因子,而且满足
b
1
+
b
2
+
…
…
+
b
k
<
=
p
r
i
m
e
F
a
c
t
o
r
s
b_{1}+b_{2}+……+b_{k}<=primeFactors
b1+b2+……+bk<=primeFactors。
好因子:如果n的一个因子可以被n的每一个质因数整除,我们称这个因子是好因子。
在找好因子个数的时候,可以这么考虑,举个例子吧。
假设
n
=
a
1
b
1
×
a
2
b
2
×
a
3
b
3
n=a_{1}^{b_{1}} \times a_{2}^{b_{2}} \times a_{3}^{b_{3}}
n=a1b1×a2b2×a3b3,
b
1
,
b
2
,
b
3
b_{1},b_{2},b_{3}
b1,b2,b3的数量都大于1,
a
1
,
a
2
,
a
3
a_{1},a_{2},a_{3}
a1,a2,a3按照从小到大排列。
那么,最小的好因子是
g
o
o
d
F
a
c
t
o
r
=
1
个
a
1
×
1
个
a
2
×
1
个
a
3
goodFactor=1个 a_{1} \times 1个 a_{2} \times 1个 a_{3}
goodFactor=1个a1×1个a2×1个a3,比它大一点的下一个好因子是
g
o
o
d
F
a
c
t
o
r
=
2
个
a
1
×
1
个
a
2
×
1
个
a
3
goodFactor=2个 a_{1} \times 1个 a_{2} \times 1个 a_{3}
goodFactor=2个a1×1个a2×1个a3,……,最大的好因子是
g
o
o
d
F
a
c
t
o
r
=
b
1
个
a
1
×
b
2
个
a
2
×
b
3
个
a
3
goodFactor=b_{1} 个 a_{1} \times b_{2} 个 a_{2} \times b_{3} 个 a_{3}
goodFactor=b1个a1×b2个a2×b3个a3,也就是n本身,那么n的好因子个数为
r
e
s
u
l
t
=
b
1
×
b
2
×
…
…
×
b
k
result=b_{1} \times b_{2} \times …… \times b_{k}
result=b1×b2×……×bk。
于是,已知原问题变成了:
b
1
+
b
2
+
…
…
+
b
k
<
=
p
r
i
m
e
F
a
c
t
o
r
s
b_{1}+b_{2}+……+b_{k}<=primeFactors
b1+b2+……+bk<=primeFactors,求解
r
e
s
u
l
t
=
b
1
×
b
2
×
…
…
×
b
k
result=b_{1} \times b_{2} \times …… \times b_{k}
result=b1×b2×……×bk的最大值。
在和一定的情况下,要想商最大,就要将primeFactors按照3个3个的分隔,分隔出来若干个3,然后相乘。
为什么是3个3个的分隔,2个2个的分隔行不行?
考虑这样一个例子:
2
+
2
+
2
=
3
+
3
,
2
×
2
×
2
<
3
×
3
2+2+2=3+3,2 \times 2 \times 2 < 3 \times 3
2+2+2=3+3,2×2×2<3×3,因为8 < 9,所以3个3个的分隔。
primeFactors对3取余可能有3种情况:0,1,2。
如果余数是0,那么就是3个3个的分隔,正好可以分配完。
如果余数是1,那么把最后一个3和余数的1,按照2和2进行拆分,因为
2
×
2
>
1
×
3
2 \times 2 > 1 \times 3
2×2>1×3。
如果余数是2,那么就把这个2乘到结果里去即可。
要求result,就要用到快速幂取模,可以在
O
(
l
o
g
2
N
)
O(log_{2}N)
O(log2N)时间内求得,还要注意在计算过程中用long,防止中间结果爆int。
class Solution {
public int maxNiceDivisors(int primeFactors) {
if (primeFactors < 4) {
return primeFactors;
}
int p = primeFactors / 3, q = primeFactors % 3, MOD = 1_000_000_007;
long result = 0;
if (q == 0) {
result = powWithMod(3, p, MOD);
} else if (q == 1) {
result = powWithMod(3, p - 1, MOD) * 2 * 2;
} else {
result = powWithMod(3, p, MOD) * 2;
}
return (int) (result % MOD);
}
private long powWithMod(int a, int b, int c) {
long result = 1, base = a % c;
while (b > 0) {
if ((b & 1) == 1) {
result = result * base % c;
}
base = base * base % c;
b >>= 1;
}
return result;
}
}