diff --git a/src/main/java/g3501_3600/s3597_partition_string/Solution.java b/src/main/java/g3501_3600/s3597_partition_string/Solution.java new file mode 100644 index 000000000..fa9910e5f --- /dev/null +++ b/src/main/java/g3501_3600/s3597_partition_string/Solution.java @@ -0,0 +1,35 @@ +package g3501_3600.s3597_partition_string; + +// #Medium #String #Hash_Table #Simulation #Trie +// #2025_06_30_Time_25_ms_(100.00%)_Space_55.91_MB_(100.00%) + +import java.util.ArrayList; +import java.util.List; + +public class Solution { + private static class Trie { + Trie[] tries = new Trie[26]; + } + + public List partitionString(String s) { + Trie trie = new Trie(); + List res = new ArrayList<>(); + Trie node = trie; + int i = 0; + int j = 0; + while (i < s.length() && j < s.length()) { + int idx = s.charAt(j) - 'a'; + if (node.tries[idx] == null) { + res.add(s.substring(i, j + 1)); + node.tries[idx] = new Trie(); + i = j + 1; + j = i; + node = trie; + } else { + node = node.tries[idx]; + j++; + } + } + return res; + } +} diff --git a/src/main/java/g3501_3600/s3597_partition_string/readme.md b/src/main/java/g3501_3600/s3597_partition_string/readme.md new file mode 100644 index 000000000..f8d6be4b9 --- /dev/null +++ b/src/main/java/g3501_3600/s3597_partition_string/readme.md @@ -0,0 +1,59 @@ +3597\. Partition String + +Medium + +Given a string `s`, partition it into **unique segments** according to the following procedure: + +* Start building a segment beginning at index 0. +* Continue extending the current segment character by character until the current segment has not been seen before. +* Once the segment is unique, add it to your list of segments, mark it as seen, and begin a new segment from the next index. +* Repeat until you reach the end of `s`. + +Return an array of strings `segments`, where `segments[i]` is the ith segment created. + +**Example 1:** + +**Input:** s = "abbccccd" + +**Output:** ["a","b","bc","c","cc","d"] + +**Explanation:** + +Here is your table, converted from HTML to Markdown: + +| Index | Segment After Adding | Seen Segments | Current Segment Seen Before? | New Segment | Updated Seen Segments | +|-------|----------------------|-----------------------|------------------------------|-------------|----------------------------------| +| 0 | "a" | [] | No | "" | ["a"] | +| 1 | "b" | ["a"] | No | "" | ["a", "b"] | +| 2 | "b" | ["a", "b"] | Yes | "b" | ["a", "b"] | +| 3 | "bc" | ["a", "b"] | No | "" | ["a", "b", "bc"] | +| 4 | "c" | ["a", "b", "bc"] | No | "" | ["a", "b", "bc", "c"] | +| 5 | "c" | ["a", "b", "bc", "c"] | Yes | "c" | ["a", "b", "bc", "c"] | +| 6 | "cc" | ["a", "b", "bc", "c"] | No | "" | ["a", "b", "bc", "c", "cc"] | +| 7 | "d" | ["a", "b", "bc", "c", "cc"] | No | "" | ["a", "b", "bc", "c", "cc", "d"] | + +Hence, the final output is `["a", "b", "bc", "c", "cc", "d"]`. + +**Example 2:** + +**Input:** s = "aaaa" + +**Output:** ["a","aa"] + +**Explanation:** + +Here is your table converted to Markdown: + +| Index | Segment After Adding | Seen Segments | Current Segment Seen Before? | New Segment | Updated Seen Segments | +|-------|----------------------|---------------|------------------------------|-------------|----------------------| +| 0 | "a" | [] | No | "" | ["a"] | +| 1 | "a" | ["a"] | Yes | "a" | ["a"] | +| 2 | "aa" | ["a"] | No | "" | ["a", "aa"] | +| 3 | "a" | ["a", "aa"] | Yes | "a" | ["a", "aa"] | + +Hence, the final output is `["a", "aa"]`. + +**Constraints:** + +* 1 <= s.length <= 105 +* `s` contains only lowercase English letters. \ No newline at end of file diff --git a/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.java b/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.java new file mode 100644 index 000000000..71d949931 --- /dev/null +++ b/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.java @@ -0,0 +1,50 @@ +package g3501_3600.s3598_longest_common_prefix_between_adjacent_strings_after_removals; + +// #Medium #Array #String #2025_06_30_Time_28_ms_(74.57%)_Space_67.08_MB_(39.11%) + +public class Solution { + private int solve(String a, String b) { + int len = Math.min(a.length(), b.length()); + int cnt = 0; + while (cnt < len && a.charAt(cnt) == b.charAt(cnt)) { + cnt++; + } + return cnt; + } + + public int[] longestCommonPrefix(String[] words) { + int n = words.length; + int[] ans = new int[n]; + if (n <= 1) { + return ans; + } + int[] lcp = new int[n - 1]; + for (int i = 0; i + 1 < n; i++) { + lcp[i] = solve(words[i], words[i + 1]); + } + int[] prefmax = new int[n - 1]; + int[] sufmax = new int[n - 1]; + prefmax[0] = lcp[0]; + for (int i = 1; i < n - 1; i++) { + prefmax[i] = Math.max(prefmax[i - 1], lcp[i]); + } + sufmax[n - 2] = lcp[n - 2]; + for (int i = n - 3; i >= 0; i--) { + sufmax[i] = Math.max(sufmax[i + 1], lcp[i]); + } + for (int i = 0; i < n; i++) { + int best = 0; + if (i >= 2) { + best = Math.max(best, prefmax[i - 2]); + } + if (i + 1 <= n - 2) { + best = Math.max(best, sufmax[i + 1]); + } + if (i > 0 && i < n - 1) { + best = Math.max(best, solve(words[i - 1], words[i + 1])); + } + ans[i] = best; + } + return ans; + } +} diff --git a/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/readme.md b/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/readme.md new file mode 100644 index 000000000..8edab2a82 --- /dev/null +++ b/src/main/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/readme.md @@ -0,0 +1,51 @@ +3598\. Longest Common Prefix Between Adjacent Strings After Removals + +Medium + +You are given an array of strings `words`. For each index `i` in the range `[0, words.length - 1]`, perform the following steps: + +* Remove the element at index `i` from the `words` array. +* Compute the **length** of the **longest common prefix** among all **adjacent** pairs in the modified array. + +Return an array `answer`, where `answer[i]` is the length of the longest common prefix between the adjacent pairs after removing the element at index `i`. If **no** adjacent pairs remain or if **none** share a common prefix, then `answer[i]` should be 0. + +**Example 1:** + +**Input:** words = ["jump","run","run","jump","run"] + +**Output:** [3,0,0,3,3] + +**Explanation:** + +* Removing index 0: + * `words` becomes `["run", "run", "jump", "run"]` + * Longest adjacent pair is `["run", "run"]` having a common prefix `"run"` (length 3) +* Removing index 1: + * `words` becomes `["jump", "run", "jump", "run"]` + * No adjacent pairs share a common prefix (length 0) +* Removing index 2: + * `words` becomes `["jump", "run", "jump", "run"]` + * No adjacent pairs share a common prefix (length 0) +* Removing index 3: + * `words` becomes `["jump", "run", "run", "run"]` + * Longest adjacent pair is `["run", "run"]` having a common prefix `"run"` (length 3) +* Removing index 4: + * words becomes `["jump", "run", "run", "jump"]` + * Longest adjacent pair is `["run", "run"]` having a common prefix `"run"` (length 3) + +**Example 2:** + +**Input:** words = ["dog","racer","car"] + +**Output:** [0,0,0] + +**Explanation:** + +* Removing any index results in an answer of 0. + +**Constraints:** + +* 1 <= words.length <= 105 +* 1 <= words[i].length <= 104 +* `words[i]` consists of lowercase English letters. +* The sum of `words[i].length` is smaller than or equal 105. \ No newline at end of file diff --git a/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.java b/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.java new file mode 100644 index 000000000..b82b6cc6e --- /dev/null +++ b/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.java @@ -0,0 +1,37 @@ +package g3501_3600.s3599_partition_array_to_minimize_xor; + +// #Medium #Array #Dynamic_Programming #Bit_Manipulation #Prefix_Sum +// #2025_06_30_Time_144_ms_(100.00%)_Space_44.80_MB_(100.00%) + +import java.util.Arrays; + +public class Solution { + public int minXor(int[] nums, int k) { + int n = nums.length; + // Step 1: Prefix XOR array + int[] pfix = new int[n + 1]; + for (int i = 1; i <= n; i++) { + pfix[i] = pfix[i - 1] ^ nums[i - 1]; + } + // Step 2: DP table + int[][] dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, Integer.MAX_VALUE); + } + for (int i = 0; i <= n; i++) { + // Base case: 1 partition + dp[i][1] = pfix[i]; + } + // Step 3: Fill DP for partitions 2 to k + for (int parts = 2; parts <= k; parts++) { + for (int end = parts; end <= n; end++) { + for (int split = parts - 1; split < end; split++) { + int segmentXOR = pfix[end] ^ pfix[split]; + int maxXOR = Math.max(dp[split][parts - 1], segmentXOR); + dp[end][parts] = Math.min(dp[end][parts], maxXOR); + } + } + } + return dp[n][k]; + } +} diff --git a/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/readme.md b/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/readme.md new file mode 100644 index 000000000..baa25d57f --- /dev/null +++ b/src/main/java/g3501_3600/s3599_partition_array_to_minimize_xor/readme.md @@ -0,0 +1,61 @@ +3599\. Partition Array to Minimize XOR + +Medium + +You are given an integer array `nums` and an integer `k`. + +Your task is to partition `nums` into `k` non-empty ****non-empty subarrays****. For each subarray, compute the bitwise **XOR** of all its elements. + +Return the **minimum** possible value of the **maximum XOR** among these `k` subarrays. + +**Example 1:** + +**Input:** nums = [1,2,3], k = 2 + +**Output:** 1 + +**Explanation:** + +The optimal partition is `[1]` and `[2, 3]`. + +* XOR of the first subarray is `1`. +* XOR of the second subarray is `2 XOR 3 = 1`. + +The maximum XOR among the subarrays is 1, which is the minimum possible. + +**Example 2:** + +**Input:** nums = [2,3,3,2], k = 3 + +**Output:** 2 + +**Explanation:** + +The optimal partition is `[2]`, `[3, 3]`, and `[2]`. + +* XOR of the first subarray is `2`. +* XOR of the second subarray is `3 XOR 3 = 0`. +* XOR of the third subarray is `2`. + +The maximum XOR among the subarrays is 2, which is the minimum possible. + +**Example 3:** + +**Input:** nums = [1,1,2,3,1], k = 2 + +**Output:** 0 + +**Explanation:** + +The optimal partition is `[1, 1]` and `[2, 3, 1]`. + +* XOR of the first subarray is `1 XOR 1 = 0`. +* XOR of the second subarray is `2 XOR 3 XOR 1 = 0`. + +The maximum XOR among the subarrays is 0, which is the minimum possible. + +**Constraints:** + +* `1 <= nums.length <= 250` +* 1 <= nums[i] <= 109 +* `1 <= k <= n` \ No newline at end of file diff --git a/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.java b/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.java new file mode 100644 index 000000000..9bc1d37bc --- /dev/null +++ b/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.java @@ -0,0 +1,110 @@ +package g3501_3600.s3600_maximize_spanning_tree_stability_with_upgrades; + +// #Hard #Greedy #Binary_Search #Graph #Union_Find #Minimum_Spanning_Tree +// #2025_06_30_Time_51_ms_(100.00%)_Space_132.43_MB_(100.00%) + +public class Solution { + public int maxStability(int n, int[][] edges, int k) { + int low = 0; + int high = 0; + for (int[] edge : edges) { + high = Math.max(high, edge[2]); + } + high *= 2; + int ans = -1; + while (low <= high) { + int mid = (low + high) / 2; + if (feasible(mid, n, edges, k)) { + ans = mid; + low = mid + 1; + } else { + high = mid - 1; + } + } + return ans; + } + + private boolean feasible(int t, int n, int[][] edges, int k) { + int[] par = new int[n]; + int[] rnk = new int[n]; + int[] comp = new int[] {n}; + for (int i = 0; i < n; i++) { + par[i] = i; + } + UnionFind uf = new UnionFind(par, rnk, comp); + int cost = 0; + int half = (t + 1) / 2; + for (int[] edge : edges) { + int u = edge[0]; + int v = edge[1]; + int s = edge[2]; + int m = edge[3]; + if (m == 1 && (s < t || !uf.union(u, v))) { + return false; + } + } + for (int[] edge : edges) { + int u = edge[0]; + int v = edge[1]; + int s = edge[2]; + int m = edge[3]; + if (m == 0 && s >= t) { + uf.union(u, v); + } + } + if (comp[0] == 1) { + return true; + } + for (int[] edge : edges) { + int u = edge[0]; + int v = edge[1]; + int s = edge[2]; + int m = edge[3]; + if (m == 0 && s >= half && s < t && uf.union(u, v)) { + cost++; + if (cost > k) { + return false; + } + } + } + return comp[0] == 1; + } + + private static class UnionFind { + int[] par; + int[] rnk; + int[] comp; + + UnionFind(int[] par, int[] rnk, int[] comp) { + this.par = par; + this.rnk = rnk; + this.comp = comp; + } + + int find(int x) { + if (par[x] != x) { + par[x] = find(par[x]); + } + return par[x]; + } + + boolean union(int a, int b) { + int ra = find(a); + int rb = find(b); + if (ra == rb) { + return false; + } + if (rnk[ra] < rnk[rb]) { + int temp = ra; + ra = rb; + rb = temp; + } + par[rb] = ra; + if (rnk[ra] == rnk[rb]) { + rnk[ra]++; + } + comp[0]--; + return true; + } + } +} diff --git a/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/readme.md b/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/readme.md new file mode 100644 index 000000000..0269b049d --- /dev/null +++ b/src/main/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/readme.md @@ -0,0 +1,65 @@ +3600\. Maximize Spanning Tree Stability with Upgrades + +Hard + +You are given an integer `n`, representing `n` nodes numbered from 0 to `n - 1` and a list of `edges`, where edges[i] = [ui, vi, si, musti]: + +* ui and vi indicates an undirected edge between nodes ui and vi. +* si is the strength of the edge. +* musti is an integer (0 or 1). If musti == 1, the edge **must** be included in the **spanning tree**. These edges **cannot** be **upgraded**. + +You are also given an integer `k`, the **maximum** number of upgrades you can perform. Each upgrade **doubles** the strength of an edge, and each eligible edge (with musti == 0) can be upgraded **at most** once. + +The **stability** of a spanning tree is defined as the **minimum** strength score among all edges included in it. + +Return the **maximum** possible stability of any valid spanning tree. If it is impossible to connect all nodes, return `-1`. + +**Note**: A **spanning tree** of a graph with `n` nodes is a subset of the edges that connects all nodes together (i.e. the graph is **connected**) _without_ forming any cycles, and uses **exactly** `n - 1` edges. + +**Example 1:** + +**Input:** n = 3, edges = [[0,1,2,1],[1,2,3,0]], k = 1 + +**Output:** 2 + +**Explanation:** + +* Edge `[0,1]` with strength = 2 must be included in the spanning tree. +* Edge `[1,2]` is optional and can be upgraded from 3 to 6 using one upgrade. +* The resulting spanning tree includes these two edges with strengths 2 and 6. +* The minimum strength in the spanning tree is 2, which is the maximum possible stability. + +**Example 2:** + +**Input:** n = 3, edges = [[0,1,4,0],[1,2,3,0],[0,2,1,0]], k = 2 + +**Output:** 6 + +**Explanation:** + +* Since all edges are optional and up to `k = 2` upgrades are allowed. +* Upgrade edges `[0,1]` from 4 to 8 and `[1,2]` from 3 to 6. +* The resulting spanning tree includes these two edges with strengths 8 and 6. +* The minimum strength in the tree is 6, which is the maximum possible stability. + +**Example 3:** + +**Input:** n = 3, edges = [[0,1,1,1],[1,2,1,1],[2,0,1,1]], k = 0 + +**Output:** \-1 + +**Explanation:** + +* All edges are mandatory and form a cycle, which violates the spanning tree property of acyclicity. Thus, the answer is -1. + +**Constraints:** + +* 2 <= n <= 105 +* 1 <= edges.length <= 105 +* edges[i] = [ui, vi, si, musti] +* 0 <= ui, vi < n +* ui != vi +* 1 <= si <= 105 +* musti is either `0` or `1`. +* `0 <= k <= n` +* There are no duplicate edges. \ No newline at end of file diff --git a/src/test/java/g3501_3600/s3597_partition_string/SolutionTest.java b/src/test/java/g3501_3600/s3597_partition_string/SolutionTest.java new file mode 100644 index 000000000..6a88714c4 --- /dev/null +++ b/src/test/java/g3501_3600/s3597_partition_string/SolutionTest.java @@ -0,0 +1,21 @@ +package g3501_3600.s3597_partition_string; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class SolutionTest { + @Test + void partitionString() { + assertThat( + new Solution().partitionString("abbccccd"), + equalTo(List.of("a", "b", "bc", "c", "cc", "d"))); + } + + @Test + void partitionString2() { + assertThat(new Solution().partitionString("aaaa"), equalTo(List.of("a", "aa"))); + } +} diff --git a/src/test/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.java b/src/test/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.java new file mode 100644 index 000000000..1f7b14c28 --- /dev/null +++ b/src/test/java/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.java @@ -0,0 +1,23 @@ +package g3501_3600.s3598_longest_common_prefix_between_adjacent_strings_after_removals; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.jupiter.api.Test; + +class SolutionTest { + @Test + void longestCommonPrefix() { + assertThat( + new Solution() + .longestCommonPrefix(new String[] {"jump", "run", "run", "jump", "run"}), + equalTo(new int[] {3, 0, 0, 3, 3})); + } + + @Test + void longestCommonPrefix2() { + assertThat( + new Solution().longestCommonPrefix(new String[] {"dog", "racer", "car"}), + equalTo(new int[] {0, 0, 0})); + } +} diff --git a/src/test/java/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.java b/src/test/java/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.java new file mode 100644 index 000000000..88f676c57 --- /dev/null +++ b/src/test/java/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.java @@ -0,0 +1,23 @@ +package g3501_3600.s3599_partition_array_to_minimize_xor; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.jupiter.api.Test; + +class SolutionTest { + @Test + void minXor() { + assertThat(new Solution().minXor(new int[] {1, 2, 3}, 2), equalTo(1)); + } + + @Test + void minXor2() { + assertThat(new Solution().minXor(new int[] {2, 3, 3, 2}, 3), equalTo(2)); + } + + @Test + void minXor3() { + assertThat(new Solution().minXor(new int[] {1, 1, 2, 3, 1}, 2), equalTo(0)); + } +} diff --git a/src/test/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.java b/src/test/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.java new file mode 100644 index 000000000..1b99f4023 --- /dev/null +++ b/src/test/java/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.java @@ -0,0 +1,31 @@ +package g3501_3600.s3600_maximize_spanning_tree_stability_with_upgrades; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.jupiter.api.Test; + +class SolutionTest { + @Test + void maxStability() { + assertThat( + new Solution().maxStability(3, new int[][] {{0, 1, 2, 1}, {1, 2, 3, 0}}, 1), + equalTo(2)); + } + + @Test + void maxStability2() { + assertThat( + new Solution() + .maxStability(3, new int[][] {{0, 1, 4, 0}, {1, 2, 3, 0}, {0, 2, 1, 0}}, 2), + equalTo(6)); + } + + @Test + void maxStability3() { + assertThat( + new Solution() + .maxStability(3, new int[][] {{0, 1, 1, 1}, {1, 2, 1, 1}, {2, 0, 1, 1}}, 0), + equalTo(-1)); + } +}