周赛题目
最大频率元素计数
给你一个由 正整数 组成的数组 nums 。返回数组 nums 中所有具有 最大 频率的元素的总频率 。元素的 频率 是指该元素在数组中出现的次数。
- 解法
- 用哈希表记录每个元素出现的次数,并维护出现次数最大值maxCount;
class Solution {
public int maxFrequencyElements(int[] nums) {
HashMap<Integer,Integer> map=new HashMap<>();
int maxCount=0;
for(int tem:nums){
int count=map.getOrDefault(tem,0);
map.put(tem,count+1);
maxCount=Math.max(maxCount,count+1);
}
int result=0;
for(Integer value:map.values()){
if (value==maxCount){
result+=maxCount;
}
}
return result;
}
}
找出数组中的美丽下标 I
给你一个下标从 0 开始的字符串 s 、字符串 a 、字符串 b 和一个整数 k 。
如果下标 i 满足以下条件,则认为它是一个 美丽下标:
0 <= i <= s.length - a.length
s[i…(i + a.length - 1)] == a
存在下标 j 使得:
0 <= j <= s.length - b.length
s[j…(j + b.length - 1)] == b
|j - i| <= k
以数组形式按 从小到大排序 返回美丽下标。
- 解法
- KMP+二分查照
class Solution {
public List<Integer> beautifulIndices(String s, String a, String b, int k) {
char[] text = s.toCharArray();
List<Integer> posA = martch(text, a.toCharArray());
List<Integer> posB = martch(text, b.toCharArray());
List<Integer> ans = new ArrayList<>();
for (int i : posA) {
int bi = lowerBound(posB, i);
if (bi < posB.size() && posB.get(bi) - i <= k ||
bi > 0 && i - posB.get(bi - 1) <= k) {
ans.add(i);
}
}
return ans;
}
private List<Integer> martch(char[] text, char[] pattern) {
int m = pattern.length;
int[] pi = new int[m];
int c = 0;
for (int i = 1; i < m; i++) {
char v = pattern[i];
while (c > 0 && pattern[c] != v) {
c = pi[c - 1];
}
if (pattern[c] == v) {
c++;
}
pi[i] = c;
}
List<Integer> res = new ArrayList<>();
c = 0;
for (int i = 0; i < text.length; i++) {
char v = text[i];
while (c > 0 && pattern[c] != v) {
c = pi[c - 1];
}
if (pattern[c] == v) {
c++;
}
if (c == m) {
res.add(i - m + 1);
c = pi[c - 1];
}
}
return res;
}
// 开区间写法
// 请看 https://www.bilibili.com/video/BV1AP41137w7/
private int lowerBound(List<Integer> nums, int target) {
int left = -1, right = nums.size(); // 开区间 (left, right)
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// nums[left] < target
// nums[right] >= target
int mid = (left + right) >>> 1;
if (nums.get(mid) < target) {
left = mid; // 范围缩小到 (mid, right)
} else {
right = mid; // 范围缩小到 (left, mid)
}
}
return right;
}
}
价值和小于等于 K 的最大数字
给你一个整数 k 和一个整数 x 。
令 s 为整数 num 的下标从 1 开始的二进制表示。我们说一个整数 num 的 价值 是满足 i % x == 0 且 s[i] 是 设置位 的 i 的数目。
请你返回 最大 整数 num ,满足从 1 到 num 的所有整数的 价值 和小于等于 k 。
- 解法
- 二分答案+数位DP
class Solution {
private int x;
private long num;
private long memo[][];
public long findMaximumNumber(long k, int x) {
this.x = x;
long left = 0;
long right = (k + 1) << x;
while (left + 1 < right) {
long mid = (left + right) >>> 1;//相当于mid=(left+right)/2
if (countDigitOne(mid) <= k) {
left = mid;
} else {
right = mid;
}
}
return left;
}
private long countDigitOne(long num) {
this.num = num;
int m = 64 - Long.numberOfLeadingZeros(num);//返回指定 long 值的二进制补码表示中最高("最左边")一位之前的零位数。 如果指定的值在其二进制补码表示中没有一位,则返回 64,换句话说,如果它等于 0。
memo = new long[m][m + 1];
for (long[] row : memo) {
Arrays.fill(row, -1);
}
return dfs(m - 1, 0, true);
}
private long dfs(int i, int cnt1, boolean isLimit) {
if (i < 0) return cnt1;
if (!isLimit && memo[i][cnt1] != -1) return memo[i][cnt1];
int up = isLimit ? (int) (num >> i & 1) : 1;
long res = 0;
for (int d = 0; d <= up; d++) {
res += dfs(i - 1, cnt1 + (d == 1 && (i + 1) % x == 0 ? 1 : 0), isLimit && d == up);
}
if (!isLimit) memo[i][cnt1] = res;
return res;
}
}
找出数组中的美丽下标 II
给你一个下标从 0 开始的字符串 s 、字符串 a 、字符串 b 和一个整数 k 。
如果下标 i 满足以下条件,则认为它是一个 美丽下标 :
0 <= i <= s.length - a.length
s[i…(i + a.length - 1)] == a
存在下标 j 使得:
0 <= j <= s.length - b.length
s[j…(j + b.length - 1)] == b
|j - i| <= k
以数组形式按 从小到大排序 返回美丽下标。
- 解法
- KMP+二分算法
class Solution {
public List<Integer> beautifulIndices(String s, String a, String b, int k) {
char[] text = s.toCharArray();
List<Integer> posA = search(text, a.toCharArray());
List<Integer> posB = search(text, b.toCharArray());
List<Integer> ans = new ArrayList<>();
for (int i : posA) {
int bi = lowerBound(posB, i);
if (bi < posB.size() && posB.get(bi) - i <= k ||
bi > 0 && i - posB.get(bi - 1) <= k) {
ans.add(i);
}
}
return ans;
}
private List<Integer> search(char[] text, char[] pattern) {
int m = pattern.length;
int[] pi = new int[m];
int c = 0;
for (int i = 1; i < m; i++) {
char v = pattern[i];
while (c > 0 && pattern[c] != v) {
c = pi[c - 1];
}
if (pattern[c] == v) {
c++;
}
pi[i] = c;
}
List<Integer> res = new ArrayList<>();
c = 0;
for (int i = 0; i < text.length; i++) {
char v = text[i];
while (c > 0 && pattern[c] != v) {
c = pi[c - 1];
}
if (pattern[c] == v) {
c++;
}
if (c == m) {
res.add(i - m + 1);
c = pi[c - 1];
}
}
return res;
}
private int lowerBound(List<Integer> nums, int target) {
int left = -1, right = nums.size();
while (left + 1 < right) {
int mid = (left + right) >>> 1;
if (nums.get(mid) < target) {
left = mid;
} else {
right = mid;
}
}
return right;
}
}
总结
- 字符串匹配主要使用KMP算法,后续需要针对这个算法进行训练直到掌握
- 数组有序时要尽量考虑用二分查找优化时间复杂度