diff --git a/src/main/kotlin/g3501_3600/s3597_partition_string/Solution.kt b/src/main/kotlin/g3501_3600/s3597_partition_string/Solution.kt new file mode 100644 index 00000000..7f9543e6 --- /dev/null +++ b/src/main/kotlin/g3501_3600/s3597_partition_string/Solution.kt @@ -0,0 +1,32 @@ +package g3501_3600.s3597_partition_string + +// #Medium #String #Hash_Table #Simulation #Trie +// #2025_06_30_Time_43_ms_(100.00%)_Space_61.32_MB_(100.00%) + +class Solution { + private class Trie { + var tries: Array = arrayOfNulls(26) + } + + fun partitionString(s: String): List { + val trie = Trie() + val res: MutableList = ArrayList() + var node: Trie = trie + var i = 0 + var j = 0 + while (i < s.length && j < s.length) { + val idx = s[j].code - 'a'.code + if (node.tries[idx] == null) { + res.add(s.substring(i, j + 1)) + node.tries[idx] = Trie() + i = j + 1 + j = i + node = trie + } else { + node = node.tries[idx]!! + j++ + } + } + return res + } +} diff --git a/src/main/kotlin/g3501_3600/s3597_partition_string/readme.md b/src/main/kotlin/g3501_3600/s3597_partition_string/readme.md new file mode 100644 index 00000000..f8d6be4b --- /dev/null +++ b/src/main/kotlin/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/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.kt b/src/main/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.kt new file mode 100644 index 00000000..5478ad29 --- /dev/null +++ b/src/main/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/Solution.kt @@ -0,0 +1,57 @@ +package g3501_3600.s3598_longest_common_prefix_between_adjacent_strings_after_removals + +// #Medium #Array #String #2025_06_30_Time_28_ms_(71.43%)_Space_81.21_MB_(71.43%) + +import kotlin.math.max +import kotlin.math.min + +class Solution { + private fun solve(a: String, b: String): Int { + val len = min(a.length, b.length) + var cnt = 0 + while (cnt < len && a[cnt] == b[cnt]) { + cnt++ + } + return cnt + } + + fun longestCommonPrefix(words: Array): IntArray { + val n = words.size + val ans = IntArray(n) + if (n <= 1) { + return ans + } + val lcp = IntArray(n - 1) + run { + var i = 0 + while (i + 1 < n) { + lcp[i] = solve(words[i], words[i + 1]) + i++ + } + } + val prefmax = IntArray(n - 1) + val sufmax = IntArray(n - 1) + prefmax[0] = lcp[0] + for (i in 1..= 2) { + best = max(best, prefmax[i - 2]) + } + if (i + 1 <= n - 2) { + best = max(best, sufmax[i + 1]) + } + if (i > 0 && i < n - 1) { + best = max(best, solve(words[i - 1], words[i + 1])) + } + ans[i] = best + } + return ans + } +} diff --git a/src/main/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/readme.md b/src/main/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/readme.md new file mode 100644 index 00000000..8edab2a8 --- /dev/null +++ b/src/main/kotlin/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/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.kt b/src/main/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.kt new file mode 100644 index 00000000..0ccc2bdb --- /dev/null +++ b/src/main/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/Solution.kt @@ -0,0 +1,38 @@ +package g3501_3600.s3599_partition_array_to_minimize_xor + +// #Medium #Array #Dynamic_Programming #Bit_Manipulation #Prefix_Sum +// #2025_06_30_Time_136_ms_(100.00%)_Space_54.66_MB_(100.00%) + +import kotlin.math.max +import kotlin.math.min + +class Solution { + fun minXor(nums: IntArray, k: Int): Int { + val n = nums.size + // Step 1: Prefix XOR array + val pfix = IntArray(n + 1) + for (i in 1..n) { + pfix[i] = pfix[i - 1] xor nums[i - 1] + } + // Step 2: DP table + val dp: Array = Array(n + 1) { IntArray(k + 1) } + for (row in dp) { + row.fill(Int.Companion.MAX_VALUE) + } + for (i in 0..n) { + // Base case: 1 partition + dp[i][1] = pfix[i] + } + // Step 3: Fill DP for partitions 2 to k + for (parts in 2..k) { + for (end in parts..n) { + for (split in parts - 1..1 <= nums[i] <= 109 +* `1 <= k <= n` \ No newline at end of file diff --git a/src/main/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.kt b/src/main/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.kt new file mode 100644 index 00000000..f7236231 --- /dev/null +++ b/src/main/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/Solution.kt @@ -0,0 +1,102 @@ +package g3501_3600.s3600_maximize_spanning_tree_stability_with_upgrades + +// #Hard #Greedy #Binary_Search #Graph #Union_Find #Minimum_Spanning_Tree +// #2025_06_30_Time_34_ms_(100.00%)_Space_152.67_MB_(100.00%) + +import kotlin.math.max + +class Solution { + fun maxStability(n: Int, edges: Array, k: Int): Int { + var low = 0 + var high = 0 + for (edge in edges) { + high = max(high, edge[2]) + } + high *= 2 + var ans = -1 + while (low <= high) { + val mid = (low + high) / 2 + if (feasible(mid, n, edges, k)) { + ans = mid + low = mid + 1 + } else { + high = mid - 1 + } + } + return ans + } + + private fun feasible(t: Int, n: Int, edges: Array, k: Int): Boolean { + val par = IntArray(n) + val rnk = IntArray(n) + val comp = intArrayOf(n) + for (i in 0..= t) { + uf.union(u, v) + } + } + if (comp[0] == 1) { + return true + } + for (edge in edges) { + val u = edge[0] + val v = edge[1] + val s = edge[2] + val 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 class UnionFind(var par: IntArray, var rnk: IntArray, var comp: IntArray) { + fun find(x: Int): Int { + if (par[x] != x) { + par[x] = find(par[x]) + } + return par[x] + } + + fun union(a: Int, b: Int): Boolean { + var ra = find(a) + var rb = find(b) + if (ra == rb) { + return false + } + if (rnk[ra] < rnk[rb]) { + val 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/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/readme.md b/src/main/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/readme.md new file mode 100644 index 00000000..0269b049 --- /dev/null +++ b/src/main/kotlin/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/kotlin/g3501_3600/s3597_partition_string/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3597_partition_string/SolutionTest.kt new file mode 100644 index 00000000..7e8811e3 --- /dev/null +++ b/src/test/kotlin/g3501_3600/s3597_partition_string/SolutionTest.kt @@ -0,0 +1,23 @@ +package g3501_3600.s3597_partition_string + +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Test + +internal class SolutionTest { + @Test + fun partitionString() { + assertThat>( + Solution().partitionString("abbccccd"), + equalTo>(mutableListOf("a", "b", "bc", "c", "cc", "d")), + ) + } + + @Test + fun partitionString2() { + assertThat>( + Solution().partitionString("aaaa"), + equalTo>(mutableListOf("a", "aa")), + ) + } +} diff --git a/src/test/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.kt new file mode 100644 index 00000000..e6053d89 --- /dev/null +++ b/src/test/kotlin/g3501_3600/s3598_longest_common_prefix_between_adjacent_strings_after_removals/SolutionTest.kt @@ -0,0 +1,24 @@ +package g3501_3600.s3598_longest_common_prefix_between_adjacent_strings_after_removals + +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Test + +internal class SolutionTest { + @Test + fun longestCommonPrefix() { + assertThat( + Solution() + .longestCommonPrefix(arrayOf("jump", "run", "run", "jump", "run")), + equalTo(intArrayOf(3, 0, 0, 3, 3)), + ) + } + + @Test + fun longestCommonPrefix2() { + assertThat( + Solution().longestCommonPrefix(arrayOf("dog", "racer", "car")), + equalTo(intArrayOf(0, 0, 0)), + ) + } +} diff --git a/src/test/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.kt new file mode 100644 index 00000000..d8029e26 --- /dev/null +++ b/src/test/kotlin/g3501_3600/s3599_partition_array_to_minimize_xor/SolutionTest.kt @@ -0,0 +1,22 @@ +package g3501_3600.s3599_partition_array_to_minimize_xor + +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Test + +internal class SolutionTest { + @Test + fun minXor() { + assertThat(Solution().minXor(intArrayOf(1, 2, 3), 2), equalTo(1)) + } + + @Test + fun minXor2() { + assertThat(Solution().minXor(intArrayOf(2, 3, 3, 2), 3), equalTo(2)) + } + + @Test + fun minXor3() { + assertThat(Solution().minXor(intArrayOf(1, 1, 2, 3, 1), 2), equalTo(0)) + } +} diff --git a/src/test/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.kt b/src/test/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.kt new file mode 100644 index 00000000..960e1fed --- /dev/null +++ b/src/test/kotlin/g3501_3600/s3600_maximize_spanning_tree_stability_with_upgrades/SolutionTest.kt @@ -0,0 +1,41 @@ +package g3501_3600.s3600_maximize_spanning_tree_stability_with_upgrades + +import org.hamcrest.CoreMatchers.equalTo +import org.hamcrest.MatcherAssert.assertThat +import org.junit.jupiter.api.Test + +internal class SolutionTest { + @Test + fun maxStability() { + assertThat( + Solution().maxStability(3, arrayOf(intArrayOf(0, 1, 2, 1), intArrayOf(1, 2, 3, 0)), 1), + equalTo(2), + ) + } + + @Test + fun maxStability2() { + assertThat( + Solution() + .maxStability( + 3, + arrayOf(intArrayOf(0, 1, 4, 0), intArrayOf(1, 2, 3, 0), intArrayOf(0, 2, 1, 0)), + 2, + ), + equalTo(6), + ) + } + + @Test + fun maxStability3() { + assertThat( + Solution() + .maxStability( + 3, + arrayOf(intArrayOf(0, 1, 1, 1), intArrayOf(1, 2, 1, 1), intArrayOf(2, 0, 1, 1)), + 0, + ), + equalTo(-1), + ) + } +}