每日一道leetcode(2026.03.28):找出对应 LCP 矩阵的字符串(这题真恶心)

每日一道leetcode(2026.03.28):找出对应 LCP 矩阵的字符串

  • [1. 题目](#1. 题目)
  • [2. 分析](#2. 分析)
  • [3. 代码实现](#3. 代码实现)
  • [4. 总结](#4. 总结)

1. 题目

对任一由 n 个小写英文字母组成的字符串 word ,我们可以定义一个 n x n 的矩阵,并满足:

lcp[i][j] 等于子字符串 word[i,...,n-1]word[j,...,n-1] 之间的最长公共前缀的长度。

给你一个 n x n 的矩阵 lcp 。返回与 lcp 对应的、按字典序最小的字符串 word

。如果不存在这样的字符串,则返回空字符串。

对于长度相同的两个字符串 a 和 b ,如果在 a 和 b 不同的第一个位置,字符串 a 的字母在字母表中出现的顺序先于 b

中的对应字母,则认为字符串 a 按字典序比字符串 b 小。例如,"aabd" 在字典上小于 "aaca"

,因为二者不同的第一位置是第三个字母,而 'b' 先于 'c' 出现。

示例 1:

输入:lcp = [[4,0,2,0],[0,3,0,1],[2,0,2,0],[0,1,0,1]] 输出:"abab" 解释:lcp

对应由两个交替字母组成的任意 4 字母字符串,字典序最小的是 "abab" 。 示例 2:

输入:lcp = [[4,3,2,1],[3,3,2,1],[2,2,2,1],[1,1,1,1]] 输出:"aaaa" 解释:lcp

对应只有一个不同字母的任意 4 字母字符串,字典序最小的是 "aaaa" 。 示例 3:

输入:lcp = [[4,3,2,1],[3,3,2,1],[2,2,2,1],[1,1,1,3]] 输出:"" 解释:lcp[3][3]

无法等于 3 ,因为 word[3,...,3] 仅由单个字母组成;因此,不存在答案。

提示

1 <= n == lcp.length == lcp[i].length <= 1000

0 <= lcp[i][j] <= n

2. 分析

这道题贼恶心:(

题目看了数遍!!

我一开始的理解是,矩阵对角线要等于n-i,然后左右两部分对称,然后再根据行对应数字映射到字母的字符序列上去,遗漏了好几个需求点。

为了便于理解,先来说道说道这个lcp,何为lcp, Longest Common Prefix,最长公共前缀。举个实际例子可能更好的理解:

比如字符串abac,对应的这个字符串有一个唯一的lcp矩阵

{4,0,1,0},

{0,3,0,0},

{1,0,2,0},

{0,0,0,1}

一言以蔽之:字符序列abac的第i和第j开始之后的子序列,有多少个相同的字符。

  • i=0,j=0时,子序列s[i]=abac,s[j]=abac,所以相同前缀长度为4,很明显,当i=j时,lcp[i][j]=子序列的长度;
  • i=0,j=1时,子序列s[i]=abac,s[j]=bac,公共前缀长度为0;
  • i=0,j=2时,子序列s[i]=abac,s[j]=ac,公共前缀长度为1;
  • i=0,j=3时,子序列s[i]=abac,s[j]=c,公共前缀长度为0;
  • 以此类推

其实站在这个角度,很好理解,但反向的从矩阵来判断符合lcp矩阵,就更有难度了。下面列举一下lcp矩阵的规则:

  • 对角线为n-i
java 复制代码
lcp[i][i] = n - i
  • 矩阵对称
java 复制代码
lcp[i][j] = lcp[j][i]
  • 长度上限
java 复制代码
lcp[i][j] ≤ min(n-i, n-j)

这个其实好理解,比如ab和abc两个子序列的最大公共序列最大也就是全匹配的情况,为2

  • 递推规则
    如果 s[i] == s[j],
    那么:lcp[i][j] = lcp[i+1][j+1] + 1
    这个规则其实没有那么明显,但提到了还是比较好理解,还是上面的例子,ab和abc两个子序列,他们的最大公共序列长度为2,那么b和bc两个子序列的最大公共长度肯定为1。

3. 代码实现

java 复制代码
class Solution {
    public String findTheString(int[][] lcp) {
        int n = lcp.length;
        for (int i = 0; i < n; i++) {
            // 对角线等于n-i
            if (lcp[i][i] != n - i) {
                System.out.println(i + "对角线不匹配");
                return "";
            }
            for (int j = 0; j < i; j++) {
                // 矩阵对角线元素相等
                if (lcp[i][j] != lcp[j][i]) {
                    System.out.println(i + "," + j + ",矩阵对角线元素不相等");
                    return "";
                }
                // 矩阵元素小于等于min(n-i,n-j)
                int min = Math.min((n - i), (n - j));
                if (!(lcp[i][j] <= min)) {
                    System.out.println(i + "," + j + ",矩阵元素大于min");
                    return "";
                }
                // 递推规则
                if (lcp[i][j] > 0 && i < n - 1 && j < n - 1) {
                    if (lcp[i][j] != lcp[i + 1][j + 1] + 1) {
                        System.out.println(i + "," + j + ",矩阵元素不满足递推");
                        return "";
                    }
                }
            }
        }
        System.out.println("矩阵满足要求");
        // 还原字典序最小的字符串
        char[] chars = new char[n];
        char letter = 'a';
        int count = 0;
        for (int i = 0; i < n; i++) {
            boolean matched = false;
            for (int j = i; j < n; j++) {
                if (i == j && chars[i] > 0) {
                    break;
                }
                if (lcp[i][j] > 0) {
                    chars[j] = letter;
                    matched = true;
                    count++;
                }
            }
            if (count == n) {
                break;
            }
            if (matched) {
                letter++;
                if (letter > 'z') {
                    // 全部由小写英文字母组成
                    System.out.println("超出小写字母序列范围");
                    return "";
                }
            }
        }
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            sb.append(aChar);
        }
        String str = sb.toString();
        char[] strArray = str.toCharArray();
        // 最终检查
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (lcp[i][j] > 0) {
                    if (strArray[i] != strArray[j]) {
                        System.out.println("字典序不满足");
                        return "";
                    }
                }
            }
        }
        return str;
    }
}

4. 总结

这道题花费了很多时间,主要是题目理解和隐藏彩蛋上。

超出规则范围的大概有两点:

一是后面字符串还原的时候,要保证所有的字符在a到z范围内,超出了,说明不符合;

二是输入的矩阵符合所有的规则,但是最终还原出来的字符串不符合预期答案。这里最恶心的就是从字符串肯定能映射到一个lcp的n*n矩阵,但是矩阵就算满足了上面提到的那些规则,还可能违背一些逻辑。比如a和b有公共前缀,a和c也有公共前缀时,那么b和c肯定有公共前缀。这一点没有列举到上面的规则中,在通过矩阵遍历来实现,维度放得很大,通过最终生成的字符串来逆向验证,反倒更加简单些。

跑出来的效率只超过了6%,可以看到代码还是有优化空间的,把上下两次全遍历合二为一,有兴趣的朋友可以尝试一下。

相关推荐
羊小猪~~2 小时前
算法/力扣--栈与队列经典题目
开发语言·c++·后端·考研·算法·leetcode·职场和发展
逆境不可逃2 小时前
LeetCode 热题 100 之 131. 分割回文串 51. N 皇后
算法·leetcode·职场和发展
阿Y加油吧2 小时前
二叉树面试送分题|力扣101对称+226翻转(递归极简写法,手写无压力)
leetcode·面试·职场和发展
We་ct2 小时前
LeetCode 373. 查找和最小的 K 对数字:题解+代码详解
前端·算法·leetcode·typescript·二分·
Tisfy12 小时前
LeetCode 2839.判断通过操作能否让字符串相等 I:if-else(两两判断)
算法·leetcode·字符串·题解
kronos.荒17 小时前
搜索二维矩阵中的target——二分查找或者二叉搜索树(python)
python·矩阵·二分查找
Aaswk17 小时前
刷题笔记(回溯算法)
数据结构·c++·笔记·算法·leetcode·深度优先·剪枝
Frostnova丶18 小时前
(11)LeetCode 239. 滑动窗口最大值
数据结构·算法·leetcode
参.商.18 小时前
【Day48】46. 全排列
leetcode·golang