diff --git a/problems/2573-find-the-string-with-lcp/analysis.md b/problems/2573-find-the-string-with-lcp/analysis.md new file mode 100644 index 0000000..04706be --- /dev/null +++ b/problems/2573-find-the-string-with-lcp/analysis.md @@ -0,0 +1,59 @@ +# 2573. Find the String with LCP + +[LeetCode Link](https://leetcode.com/problems/find-the-string-with-lcp/) + +Difficulty: Hard +Topics: Array, String, Dynamic Programming, Greedy, Union-Find, Matrix +Acceptance Rate: 41.0% + +## Hints + +### Hint 1 + +Think about what the LCP matrix tells you about character equality. If `lcp[i][j] > 0`, what does that say about the characters at positions `i` and `j`? This relationship can help you group positions together. + +### Hint 2 + +Use a greedy strategy to assign characters. Start with 'a' at the first unassigned position, and propagate: any position `j` where `lcp[i][j] > 0` must share the same character as position `i`. This is essentially a Union-Find / grouping problem. After building the string greedily, you need to verify it actually produces the given LCP matrix. + +### Hint 3 + +The verification step is the critical insight. After constructing a candidate string, recompute the LCP matrix using dynamic programming from the bottom-right corner: if `word[i] == word[j]`, then `lcp[i][j] = lcp[i+1][j+1] + 1` (with boundary handling). If the recomputed matrix doesn't match the input, no valid string exists. This DP is the same recurrence that defines LCP for suffixes. + +## Approach + +The algorithm has two phases: **construction** and **verification**. + +**Phase 1 — Greedy Construction:** + +Iterate through positions `0` to `n-1`. For each unassigned position `i`, assign the smallest available character (starting from `'a'`). Then scan all positions `j > i`: if `lcp[i][j] > 0`, position `j` must have the same character as `i` (since a positive LCP means the suffixes starting at `i` and `j` share at least their first character). If `j` was already assigned a different character, or if we exhaust all 26 letters, return `""`. + +**Phase 2 — Verification:** + +The greedy phase only enforces a necessary condition (character equality from `lcp > 0`). It doesn't verify the exact LCP values. We recompute the full LCP matrix from the constructed string using the DP recurrence: + +- If `word[i] == word[j]`: `lcp[i][j] = lcp[i+1][j+1] + 1` (or `1` if at boundary) +- If `word[i] != word[j]`: `lcp[i][j] = 0` + +Process from bottom-right to top-left. If the recomputed matrix matches the input exactly, return the string; otherwise return `""`. + +**Example walkthrough with `lcp = [[4,0,2,0],[0,3,0,1],[2,0,2,0],[0,1,0,1]]`:** + +- Position 0: unassigned → assign `'a'`. Check lcp[0][j]: lcp[0][1]=0, lcp[0][2]=2>0 → assign `'a'` to position 2. lcp[0][3]=0. +- Position 1: unassigned → assign `'b'`. Check lcp[1][j]: lcp[1][2]=0, lcp[1][3]=1>0 → assign `'b'` to position 3. +- Positions 2,3 already assigned. Result: `"abab"`. +- Verify: recomputed LCP matches input. Return `"abab"`. + +## Complexity Analysis + +Time Complexity: O(n²) — both the greedy scan and the verification DP iterate over all pairs (i, j). + +Space Complexity: O(n²) — for the verification LCP matrix. The string itself is O(n). + +## Edge Cases + +- **Diagonal mismatch:** `lcp[i][i]` must equal `n - i` (a suffix's LCP with itself is its own length). If violated, return `""`. +- **Asymmetry:** `lcp[i][j]` must equal `lcp[j][i]`. The verification step catches this. +- **Value too large:** `lcp[i][j] > n - max(i,j)` is impossible. The verification catches this too. +- **More than 26 groups:** If the matrix implies more than 26 distinct character groups, we run out of letters. Return `""`. +- **n = 1:** Single character. `lcp` must be `[[1]]`, answer is `"a"`. diff --git a/problems/2573-find-the-string-with-lcp/problem.md b/problems/2573-find-the-string-with-lcp/problem.md new file mode 100644 index 0000000..fc2bb1b --- /dev/null +++ b/problems/2573-find-the-string-with-lcp/problem.md @@ -0,0 +1,65 @@ +--- +number: "2573" +frontend_id: "2573" +title: "Find the String with LCP" +slug: "find-the-string-with-lcp" +difficulty: "Hard" +topics: + - "Array" + - "String" + - "Dynamic Programming" + - "Greedy" + - "Union-Find" + - "Matrix" +acceptance_rate: 4100.8 +is_premium: false +created_at: "2026-03-28T03:20:43.124847+00:00" +fetched_at: "2026-03-28T03:20:43.124847+00:00" +link: "https://leetcode.com/problems/find-the-string-with-lcp/" +date: "2026-03-28" +--- + +# 2573. Find the String with LCP + +We define the `lcp` matrix of any **0-indexed** string `word` of `n` lowercase English letters as an `n x n` grid such that: + + * `lcp[i][j]` is equal to the length of the **longest common prefix** between the substrings `word[i,n-1]` and `word[j,n-1]`. + + + +Given an `n x n` matrix `lcp`, return the alphabetically smallest string `word` that corresponds to `lcp`. If there is no such string, return an empty string. + +A string `a` is lexicographically smaller than a string `b` (of the same length) if in the first position where `a` and `b` differ, string `a` has a letter that appears earlier in the alphabet than the corresponding letter in `b`. For example, `"aabd"` is lexicographically smaller than `"aaca"` because the first position they differ is at the third letter, and `'b'` comes before `'c'`. + + + +**Example 1:** + + + **Input:** lcp = [[4,0,2,0],[0,3,0,1],[2,0,2,0],[0,1,0,1]] + **Output:** "abab" + **Explanation:** lcp corresponds to any 4 letter string with two alternating letters. The lexicographically smallest of them is "abab". + + +**Example 2:** + + + **Input:** lcp = [[4,3,2,1],[3,3,2,1],[2,2,2,1],[1,1,1,1]] + **Output:** "aaaa" + **Explanation:** lcp corresponds to any 4 letter string with a single distinct letter. The lexicographically smallest of them is "aaaa". + + +**Example 3:** + + + **Input:** lcp = [[4,3,2,1],[3,3,2,1],[2,2,2,1],[1,1,1,3]] + **Output:** "" + **Explanation:** lcp[3][3] cannot be equal to 3 since word[3,...,3] consists of only a single letter; Thus, no answer exists. + + + + +**Constraints:** + + * `1 <= n == ``lcp.length == ``lcp[i].length` `<= 1000` + * `0 <= lcp[i][j] <= n` diff --git a/problems/2573-find-the-string-with-lcp/solution_daily_20260328.go b/problems/2573-find-the-string-with-lcp/solution_daily_20260328.go new file mode 100644 index 0000000..b9c99d5 --- /dev/null +++ b/problems/2573-find-the-string-with-lcp/solution_daily_20260328.go @@ -0,0 +1,53 @@ +package main + +// Greedy construction + DP verification. +// 1. Assign characters greedily: for each unassigned position, use the next +// letter. Propagate: if lcp[i][j] > 0, positions i and j share a character. +// 2. Verify by recomputing the LCP matrix via DP and comparing with input. +func findTheString(lcp [][]int) string { + n := len(lcp) + word := make([]byte, n) + + var c byte = 'a' + for i := 0; i < n; i++ { + if word[i] != 0 { + continue + } + if c > 'z' { + return "" + } + word[i] = c + for j := i + 1; j < n; j++ { + if lcp[i][j] > 0 { + if word[j] != 0 && word[j] != c { + return "" + } + word[j] = c + } + } + c++ + } + + // Verify: recompute LCP matrix from the constructed string and compare. + actual := make([][]int, n) + for i := range actual { + actual[i] = make([]int, n) + } + + for i := n - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + if word[i] == word[j] { + if i+1 < n && j+1 < n { + actual[i][j] = actual[i+1][j+1] + 1 + } else { + actual[i][j] = 1 + } + } + if actual[i][j] != lcp[i][j] { + return "" + } + } + } + + return string(word) +} diff --git a/problems/2573-find-the-string-with-lcp/solution_daily_20260328_test.go b/problems/2573-find-the-string-with-lcp/solution_daily_20260328_test.go new file mode 100644 index 0000000..1743afe --- /dev/null +++ b/problems/2573-find-the-string-with-lcp/solution_daily_20260328_test.go @@ -0,0 +1,56 @@ +package main + +import "testing" + +func TestFindTheString(t *testing.T) { + tests := []struct { + name string + lcp [][]int + expected string + }{ + { + name: "example 1: alternating pattern abab", + lcp: [][]int{{4, 0, 2, 0}, {0, 3, 0, 1}, {2, 0, 2, 0}, {0, 1, 0, 1}}, + expected: "abab", + }, + { + name: "example 2: all same character aaaa", + lcp: [][]int{{4, 3, 2, 1}, {3, 3, 2, 1}, {2, 2, 2, 1}, {1, 1, 1, 1}}, + expected: "aaaa", + }, + { + name: "example 3: invalid lcp[3][3] = 3", + lcp: [][]int{{4, 3, 2, 1}, {3, 3, 2, 1}, {2, 2, 2, 1}, {1, 1, 1, 3}}, + expected: "", + }, + { + name: "edge case: single character", + lcp: [][]int{{1}}, + expected: "a", + }, + { + name: "edge case: invalid single element lcp[0][0] != 1", + lcp: [][]int{{2}}, + expected: "", + }, + { + name: "edge case: asymmetric matrix", + lcp: [][]int{{2, 1}, {0, 1}}, + expected: "", + }, + { + name: "edge case: two distinct characters", + lcp: [][]int{{2, 0}, {0, 1}}, + expected: "ab", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := findTheString(tt.lcp) + if result != tt.expected { + t.Errorf("got %q, want %q", result, tt.expected) + } + }) + } +}