Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions problems/2573-find-the-string-with-lcp/analysis.md
Original file line number Diff line number Diff line change
@@ -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"`.
65 changes: 65 additions & 0 deletions problems/2573-find-the-string-with-lcp/problem.md
Original file line number Diff line number Diff line change
@@ -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`
53 changes: 53 additions & 0 deletions problems/2573-find-the-string-with-lcp/solution_daily_20260328.go
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}