当前位置: 首页 > article >正文

【Leetcode 每日一题】3291. 形成目标字符串需要的最少字符串数 I

问题背景

给你一个字符串数组 w o r d s words words 和一个字符串 t a r g e t target target
如果字符串 x x x w o r d s words words任意 字符串的 前缀(字符串的前缀是从字符串的开头开始并延伸到其中任意点的子串),则认为 x x x 是一个 有效 字符串。
现计划通过 连接 有效字符串形成 t a r g e t target target,请你计算并返回需要连接的 最少 字符串数量。如果无法通过这种方式形成 t a r g e t target target,则返回 − 1 -1 1

数据约束

  • 1 ≤ w o r d s . l e n g t h ≤ 100 1 \le words.length \le 100 1words.length100
  • 1 ≤ w o r d s [ i ] . l e n g t h ≤ 5 × 1 0 3 1 \le words[i].length \le 5 \times 10 ^ 3 1words[i].length5×103
  • s u m ( w o r d s [ i ] . l e n g t h ) ≤ 1 0 5 sum(words[i].length) \le 10 ^ 5 sum(words[i].length)105
  • w o r d s [ i ] words[i] words[i] 只包含小写英文字母
  • 1 ≤ t a r g e t . l e n g t h ≤ 5 × 1 0 3 1 \le target.length \le 5 \times 10 ^ 3 1target.length5×103
  • t a r g e t target target 只包含小写英文字母

解题过程

周赛第三题的水准,数据范围允许暴力解,似乎可以用前缀树搭配嵌套循环解决。遗憾的是我目前只会写前缀树,还不会用前缀树来解决问题。
既然要学,那就学习一下一般情形化的做法。这题可以看作将目标分割成几个部分,每个部分都是给定的数组中字符串的前缀。
要求选用的字符串尽可能少,就要每次覆盖的范围尽可能大,这就可以参考 跳跃游戏 II 和 灌溉花园的最少水龙头数目。没有保证一定能实现目标,要单独处理。

分割的部分,需要用到 扩展 KMP 算法,也叫字符串的 Z 函数,它的作用是求出一个字符串中各个后缀与它本身的最长公共前缀的长度。这个东西今天是第一次见,不要求马上能学会,先见识一下。
还有使用字符串哈希和 AC 自动机的做法,因为相应的算法还没有掌握,先不作要求,可以暂时把重点放在吃透造桥问题的贪心做法上。

具体实现

class Solution {
    public int minValidStrings(String[] words, String target) {
        int n = target.length();
        int[] maxJumps = new int[n];
        for(String word : words) {
            // 把目标拼到这个单词的后面,就可以求出 Z 函数来辅助计算每个字符串可取的最大前缀了
            // 加一个特殊字符,防止下标越界
            int[] z = calcZ(word + "#" + target);
            int m = word.length() + 1; // 这里额外加的长度,是用来修正特殊字符的
            // 求出目标每个位置上能够匹配到的最大前缀长度
            for(int i = 0; i < n; i ++) {
                maxJumps[i] = Math.max(maxJumps[i], z[m + i]);
            }
        }
        return jump(maxJumps);
    }

    // Z 函数
    private int[] calcZ(String s) {
        char[] chS = s.toCharArray();
        int n = chS.length;
        int[] z = new int[n];
        int left = 0, right = 0;
        for(int i = 1; i < n; i++) {
            if(i <= right) {
                z[i] = Math.min(z[i - left], right - i + 1);
            }
            while(i + z[i] < n && chS[z[i]] == chS[i + z[i]]) {
                left = i;
                right = i + z[i];
                z[i]++;
            }
        }
        return z;
    }

    // 造桥问题的解,参见 Leetcode 45.跳跃游戏 II 和 Leetcode 1326.灌溉花园的最少水龙头数目
    private int jump(int[] maxJumps) {
        int res = 0;
        int curEnd = 0;
        int nextEnd = 0;
        for(int i = 0; i < maxJumps.length; i++) {
            nextEnd = Math.max(nextEnd, i + maxJumps[i]);
            if(i == curEnd) {
                if(i == nextEnd) {
                    return -1;
                }
                curEnd = nextEnd;
                res++;
            }
        }
        return res;
    }
}

http://www.kler.cn/a/440969.html

相关文章:

  • 如何从客观角度批判性阅读分析博客
  • 洛谷 P10288 [GESP样题 八级] 区间 C++ 完整题解(STL二分法)
  • matlab的.mat文件怎么把表格中的值全部设置为空
  • 【设计测试用例自动化测试性能测试 实战篇】
  • maven、npm、pip、yum官方镜像修改文档
  • 01-时间与管理
  • 建站经验:服务器同步与数据库备份的终极解决方案
  • Arrys.asList踩坑实录
  • Linux计算时间差
  • 【LeetCode】2381、字母移位 II
  • Three.js相机Camera控件知识梳理
  • 压力测试Jmeter简介
  • Java全栈项目实战:校园论坛社交平台开发
  • Vue3实现双向绑定的基本原理和代码示例解析
  • HAL库与标准库的GPIO配置结构体对比
  • 免费下载 | 2024全球AI网络安全产品洞察报告
  • Python-基于Pygame的小游戏(贪吃蛇)(一)
  • Flink State面试题和参考答案-(下)
  • 数智读书笔记系列003 深度学习革命 从历史到未来
  • C++ ofstream:写操作
  • 如何在服务器上安装 Maven
  • busybox学习——简单介绍
  • 学习记录(13):VR晕动症-VR Motion Sickness
  • springcloud eureka原理和机制
  • 吉利百度发表联合声明:将积极协助极越处理相关事宜
  • HIK 相机 设置缓存节点进行取流