给你一个整数数组 nums
。请你将 nums
中每一个元素都替换为它的各个数位之 和 。
请你返回替换所有元素以后 nums
中的 最小 元素。
示例 1:
输入:nums = [10,12,13,14]
输出:1
解释:
nums
替换后变为 [1, 3, 4, 5]
,最小元素为 1 。
示例 2:
输入:nums = [1,2,3,4]
输出:1
解释:
nums
替换后变为 [1, 2, 3, 4]
,最小元素为 1 。
示例 3:
输入:nums = [999,19,199]
输出:10
解释:
nums
替换后变为 [27, 10, 19]
,最小元素为 10 。
提示:
1 <= nums.length <= 100
1 <= nums[i] <= 10^4
思路:看代码即可。
代码:
class Solution {
public int minElement(int[] nums) {
int minSum = Integer.MAX_VALUE;
for (int num : nums) {
int digitSum = getDigitSum(num);
minSum = Math.min(minSum, digitSum);
}
return minSum;
}
private static int getDigitSum(int num) {
int sum = 0;
while (num > 0) {
sum += num % 10; // 取出最后一位数字
num /= 10; // 去掉最后一位数字
}
return sum;
}
}
给你一个数组 maximumHeight
,其中 maximumHeight[i]
表示第 i
座塔可以达到的 最大 高度。
你的任务是给每一座塔分别设置一个高度,使得:
- 第
i
座塔的高度是一个正整数,且不超过maximumHeight[i]
。 - 所有塔的高度互不相同。
请你返回设置完所有塔的高度后,可以达到的 最大 总高度。如果没有合法的设置,返回 -1
。
示例 1:
输入:maximumHeight = [2,3,4,3]
输出:10
解释:我们可以将塔的高度设置为:[1, 2, 4, 3]
。
示例 2:
输入:maximumHeight = [15,10]
输出:25
解释:我们可以将塔的高度设置为:[15, 10]
。
示例 3:
输入:maximumHeight = [2,2,1]
输出:-1
解释:无法设置塔的高度为正整数且高度互不相同。
提示:
1 <= maximumHeight.length <= 10^5
1 <= maximumHeight[i] <= 10^9
思路:
从最大的元素开始思考:数组中的最大值 m 不变是最好的。数组中的次大值呢?如果它等于 m,那么它必须变成 m−1,否则不变。
为了方便计算,先把数组从大到小排序,那么 maximumHeight[i] 的实际值为min(maximumHeight[i],maximumHeight[i−1]−1)
如果元素值 ≤0,不符合题目要求,返回 −1。最终答案为 maximumHeight 的元素之和
代码:
class Solution {
public long maximumTotalSum(int[] maximumHeight) {
Arrays.sort(maximumHeight); // 从小到大排序,下面倒着遍历
int n = maximumHeight.length;
long ans = maximumHeight[n - 1];
for (int i = n - 2; i >= 0; i--) {
maximumHeight[i] = Math.min(maximumHeight[i], maximumHeight[i + 1] - 1);
if (maximumHeight[i] <= 0) {
return -1;
}
ans += maximumHeight[i];
}
return ans;
}
}
给你两个字符串 word1
和 word2
。如果一个字符串 x
修改 至多 一个字符会变成 y
,那么我们称它与 y
几乎相等 。
如果一个下标序列 seq
满足以下条件,我们称它是 合法的 :
- 下标序列是 升序 的。
- 将
word1
中这些下标对应的字符 按顺序 连接,得到一个与word2
几乎相等 的字符串。
请你返回一个长度为 word2.length
的数组,表示一个 字典序最小的合法下标序列。如果不存在这样的序列,请你返回一个 空 数组。
注意 ,答案数组必须是字典序最小的下标数组,而 不是 由这些下标连接形成的字符串。
示例 1:
输入:word1 = "vbcca", word2 = "abc"
输出:[0,1,2]
解释:字典序最小的合法下标序列为 [0, 1, 2]
:
- 将
word1[0]
变为'a'
。 word1[1]
已经是'b'
。word1[2]
已经是'c'
。
示例 2:
输入:word1 = "bacdc", word2 = "abc"
输出:[1,2,4]
解释:字典序最小的合法下标序列为 [1, 2, 4]
:
word1[1]
已经是'a'
。- 将
word1[2]
变为'b'
。 word1[4]
已经是'c'
。
示例 3:
输入:word1 = "aaaaaa", word2 = "aaabc"
输出:[]
解释:没有合法的下标序列。
示例 4:
输入:word1 = "abc", word2 = "ab"
输出:[0,1]
提示:
1 <= word2.length < word1.length <= 3 * 10^5
word1
和word2
只包含小写英文字母。
思路:本题可以修改一个字母,推荐先完成不修改版本 2565. 最少得分子序列(参考灵神的题解)。做完 2565 后,你知道本题也可以用前后缀分解,但难点在于计算字典序最小的下标序列。
为方便描述,下文把 word1记作 s,把 word2记作 t。
定义 suf[i] 为 s[i:] 对应的 t 的最长后缀的开始下标 j,即 t[j:] 是 s[i:] 的子序列。
预处理 suf,然后从左到右遍历 s,分类讨论:
如果 s[i]=t[j],既然能匹配上,那么就立刻匹配,直接把 i 加入答案。(如果不匹配,可能后面就没机会找到子序列了。)
如果 s[i]不等于t[j] 且 suf[i+1]≤j+1,说明修改 s[i] 为 t[j] 后,t[j+1:] 是 s[i+1:] 的子序列。此时一定要修改,如果不修改,那么答案的第 j 个下标就比 i 大了,不是字典序最小的下标序列。
修改后,继续向后匹配,在 s[i]=t[j] 时把 i 加入答案。
循环中,如果发现 j 等于 t 的长度,说明匹配完成,立刻返回答案。
如果循环中没有返回,那么循环结束后返回空数组。
代码:
class Solution {
public int[] validSequence(String S, String T) {
char[] s = S.toCharArray();
char[] t = T.toCharArray();
int n = s.length;
int m = t.length;
int[] suf = new int[n + 1];
suf[n] = m;
int j = m - 1;
for (int i = n - 1; i >= 0; i--) {
if (j >= 0 && s[i] == t[j]) {
j--;
}
suf[i] = j + 1;
}
int[] ans = new int[m];
boolean changed = false; // 是否修改过
j = 0;
for (int i = 0; i < n; i++) {
if (s[i] == t[j] || !changed && suf[i + 1] <= j + 1) {
if (s[i] != t[j]) {
changed = true;
}
ans[j++] = i;
if (j == m) {
return ans;
}
}
}
return new int[]{};
}
}
给你两个字符串 s
和 pattern
。如果一个字符串 x
修改 至多 一个字符会变成 y
,那么我们称它与 y
几乎相等 。
请你返回 s
中下标 最小 的 子字符串,它与 pattern
几乎相等 。如果不存在,返回 -1
。
子字符串 是字符串中的一个 非空、连续的字符序列。
示例 1:
输入:s = "abcdefg", pattern = "bcdffg"
输出:1
解释:将子字符串 s[1..6] == "bcdefg"
中 s[4]
变为 "f"
,得到 "bcdffg"
。
示例 2:
输入:s = "ababbababa", pattern = "bacaba"
输出:4
解释:将子字符串 s[4..9] == "bababa"
中 s[6]
变为 "c"
,得到 "bacaba"
。
示例 3:
输入:s = "abcd", pattern = "dba"
输出:-1
示例 4:
输入:s = "dde", pattern = "d"
输出:0
提示:
1 <= pattern.length < s.length <= 3 * 10^5
s
和pattern
都只包含小写英文字母。
思路:思路和周赛第三题一样,采用前后缀分解解决。
现在变成了两个问题:
第一个问题:对于每个从 s[i] 开始的字符串 s[i..],计算它能匹配 pattern 多长的前缀。
第二个问题:对于每个以 s[j] 结尾的字符串 s[..j],计算它能匹配 pattern 多长的后缀。
比如示例 1,s=abcdefg, pattern=bcdffg,其中:
s[1..] 可以匹配 pattern 长为 3 的前缀。
s[..6] 可以匹配 pattern 长为 2 的后缀。
那么 3+2=5 等于 pattern 的长度减一,我们可以修改一个字母使得 s[1..6] 与 pattern 相等。
对于第一个问题,我们可以构造字符串 pattern+s,计算其 Z 数组 preZ。那么 s[i..] 与 pattern 前缀可以匹配的最长长度为 preZ[i+m],其中 m 为 pattern 的长度。
对于第二个问题,我们可以构造字符串 rev(pattern)+rev(s),计算其 Z 数组,再反转 Z 数组,得到 sufZ。其中 rev(s) 表示 s 反转后的字符串。那么 s[..j] 与 pattern 后缀可以匹配的最长长度为 sufZ[j]。
设 n 为 s 的长度,m 为 pattern 的长度。
回到原问题,我们枚举 i=0,1,⋯,n−m,那么当前需要匹配的子串为 s[i..i+m−1],对应的 Z 数组元素为 preZ[i+m] 和 sufZ[i+m−1]。
如果preZ[i+m]+sufZ[i+m−1]≥m−1,那么答案为 i。
代码实现时,可以改成枚举 i=m,m+1,⋯,n,这样上面的式子就可以简化为
preZ[i]+sufZ[i−1]≥m−1
答案为 i−m。
如果没有找到匹配,返回 −1。
代码:
class Solution {
public int minStartingIndex(String s, String pattern) {
int[] preZ = calcZ(pattern + s);
int[] sufZ = calcZ(rev(pattern) + rev(s));
// 可以不反转 sufZ,下面写 sufZ[sufZ.length - i]
int n = s.length();
int m = pattern.length();
for (int i = m; i <= n; i++) {
if (preZ[i] + sufZ[sufZ.length - i] >= m - 1) {
return i - m;
}
}
return -1;
}
private int[] calcZ(String S) {
char[] s = S.toCharArray();
int n = s.length;
int[] z = new int[n];
int boxL = 0;
int boxR = 0; // z-box 左右边界
for (int i = 1; i < n; i++) {
if (i <= boxR) {
z[i] = Math.min(z[i - boxL], boxR - i + 1);
}
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
boxL = i;
boxR = i + z[i];
z[i]++;
}
}
return z;
}
private String rev(String s) {
return new StringBuilder(s).reverse().toString();
}
}