5.29 字符串、哈希

This commit is contained in:
zhangsan 2025-05-29 15:34:47 +08:00
parent 6dc7b98fe0
commit 60e38e3d55
8 changed files with 300 additions and 0 deletions

View File

@ -0,0 +1,70 @@
package hash;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 题目 18. 四数之和 ( fourSum)
* 描述给你一个由 n 个整数组成的数组 nums 和一个目标值 target 请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] 若两个四元组元素一一对应则认为两个四元组重复
*
* 0 <= a, b, c, d < n
* abc d 互不相同
* nums[a] + nums[b] + nums[c] + nums[d] == target
* 你可以按 任意顺序 返回答案
*
示例 1
输入nums = [1,0,-1,0,-2,2], target = 0
输出[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
* 链接https://leetcode.cn/problems/4sum/
*/
public class FourSum {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
int n = nums.length;
if (n < 4) return res;
Arrays.sort(nums);
for (int i = 0; i < n - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 去重 1
// 剪枝 1最小可能值都比 target 直接结束
long min1 = (long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3];
if (min1 > target) break;
// 剪枝 2当前 nums[i] + 3 个最大的值都小于 target提前进入下一轮
long max1 = (long) nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3];
if (max1 < target) continue;
for (int j = i + 1; j < n - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) continue; // 去重 2
// 同理再剪枝
long min2 = (long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2];
if (min2 > target) break;
long max2 = (long) nums[i] + nums[j] + nums[n - 1] + nums[n - 2];
if (max2 < target) continue;
int left = j + 1, right = n - 1;
while (left < right) {
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
// **指针移动后再去重** 结构直观
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return res;
}
}

View File

@ -0,0 +1,65 @@
package hash;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 题目 454. 四数相加 II ( fourSumCount)
* 描述给你四个整数数组 nums1nums2nums3 nums4 数组长度都是 n 请你计算有多少个元组 (i, j, k, l) 能满足
*
* 0 <= i, j, k, l < n
* nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
*
示例 1
输入nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出2
解释
两个元组如下
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
* 链接https://leetcode.cn/problems/4sum-ii/
*/
//超时
public class FourSumCount {
/**
* 思路把四数拆成两对 (A+B) (C+D)
* 先枚举 A B 的所有和
* HashMap<Integer, Integer> 记录 出现次数
* 再枚举 C D 的所有和
* 对于每个和 s = c + d要凑成 0 就需要 -(c+d)
* 看看 map 里有没有 -(c+d)若有就把出现次数累加到结果里
* 时间复杂度
* 两个 n² 循环 O(n²)
* 空间复杂度
* 哈希表最多 n² 个键 O(n²)
* @param nums1
* @param nums2
* @param nums3
* @param nums4
* @return
*/
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// 1) 统计 A+B 的所有组合和及其出现次数
Map<Integer, Integer> abCount = new HashMap<>();
for (int a : nums1) {
for (int b : nums2) {
int sum = a + b;
abCount.put(sum, abCount.getOrDefault(sum, 0) + 1);
}
}
// 2) 枚举 C+D -(C+D) 是否出现在 map
int ans = 0;
for (int c : nums3) {
for (int d : nums4) {
int sum = c + d;
// 需要 -(c+d) 才能凑成 0
ans += abCount.getOrDefault(-sum, 0);
}
}
return ans;
}
}

View File

@ -0,0 +1,33 @@
package hash;
import java.util.HashSet;
/**
* 题目 349. 两个数组的交集 ( Intersection)
* 描述给定两个数组 nums1 nums2 返回 它们的 交集 输出结果中的每个元素一定是 唯一 我们可以 不考虑输出结果的顺序
*
示例 1
输入nums1 = [1,2,2,1], nums2 = [2,2]
输出[2]
* 链接https://leetcode.cn/problems/intersection-of-two-arrays/
*/
public class Intersection {
public int[] intersection(int[] nums1, int[] nums2) {
// nums1 转成集合
HashSet<Integer> set1 = new HashSet<>();
for (int n : nums1) set1.add(n);
// 用第二个集合保存交集天然保证唯一
HashSet<Integer> resSet = new HashSet<>();
for (int n : nums2) {
if (set1.contains(n)) resSet.add(n);
}
int[] arr = new int[resSet.size()];
int j = 0;
for(int i : resSet){
arr[j++] = i;
}
// Set<Integer> int[]
return arr;
}
}

View File

@ -0,0 +1,30 @@
package string;
/**
* 题目 459. 重复的子字符串 (repeatedSubstringPattern)
* 描述给定一个非空的字符串 s 检查是否可以通过由它的一个子串重复多次构成
*
* 示例 1
输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成
* 链接https://leetcode.cn/problems/reverse-string-ii/
*/
public class RepeatedSubstringPattern {
public boolean repeatedSubstringPattern(String s) {
int len = s.length();
for (int j = 1; j <= len / 2; j++) {
if (len % j != 0) continue; // 只有能整除时才有可能
String tp = s.substring(0, j);
boolean flag = true;
for (int i = j; i < len; i += j) {
if (!s.substring(i, i + j).equals(tp)) {
flag = false;
break;
}
}
if (flag) return true;
}
return false;
}
}

View File

@ -0,0 +1,42 @@
package string;
/**
* 题目 541. 反转字符串 II (reverseStr)
* 描述给定一个字符串 s 和一个整数 k从字符串开头算起每计数至 2k 个字符就反转这 2k 字符中的前 k 个字符
*
* 如果剩余字符少于 k 则将剩余字符全部反转
* 如果剩余字符小于 2k 但大于或等于 k 则反转前 k 个字符其余字符保持原样
*
* 示例 1
输入s = "abcdefg", k = 2
输出"bacdfeg"
* 链接https://leetcode.cn/problems/reverse-string-ii/
*/
public class ReverseStr {
public String reverseStr(String s, int k) {
if (s == null || s.length() <= 1 || k <= 1) return s;
char[] ch = s.toCharArray();
int n = ch.length;
// i 每次跳过 2k指向每段的起点
for (int i = 0; i < n; i += 2 * k) {
// 计算本次需要翻转的区间 [i, right]
int left = i;
int right = Math.min(i + k - 1, n - 1);
reverse(ch, left, right);
}
return new String(ch);
}
/** 原地翻转 ch[l..r] */
private void reverse(char[] ch, int l, int r) {
while (l < r) {
char tmp = ch[l];
ch[l] = ch[r];
ch[r] = tmp;
l++;
r--;
}
}
}

View File

@ -0,0 +1,26 @@
package string;
/**
* 题目 344. 反转字符串 (reverseString)
* 描述编写一个函数其作用是将输入的字符串反转过来输入字符串以字符数组 s 的形式给出
* 不要给另外的数组分配额外的空间你必须原地修改输入数组使用 O(1) 的额外空间解决这一问题
*
*
* 示例 1
输入s = ["h","e","l","l","o"]
输出["o","l","l","e","h"]
* 链接https://leetcode.cn/problems/reverse-string/
*/
public class ReverseString {
public void reverseString(char[] s) {
int l = 0;
int r = s.length - 1;
while(l < r){
char temp = s[l];
s[l] = s[r];
s[r] = temp;
l++;
r--;
}
}
}

View File

@ -0,0 +1,19 @@
package hash;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.*;
public class FourSumTest {
@Test
public void fourSum() {
FourSum fourSum = new FourSum();
int[]a={2,2,2,2,2};
int target=8;
List<List<Integer>>RES=fourSum.fourSum(a,target);
System.out.println(RES.toString());
}
}

View File

@ -0,0 +1,15 @@
package string;
import org.junit.Test;
import static org.junit.Assert.*;
public class RepeatedSubstringPatternTest {
@Test
public void repeatedSubstringPattern() {
RepeatedSubstringPattern solution = new RepeatedSubstringPattern();
String s="abac";
solution.repeatedSubstringPattern(s);
}
}