判断子序列[简单]

news/2024/7/20 20:41:51 标签: 算法, leetcode, 哈希算法, 数据结构, 深度优先

优质博文:IT-BLOG-CN

一、题目

给定字符串st,判断s是否为t的子序列。字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如aceabcde的一个子序列,而aec不是)。

进阶: 如果有大量输入的S,称作S1,S2,..., Sk其中k >= 10亿,你需要依次检查它们是否为T的子序列。在这种情况下,你会怎样改变代码?

示例 1:
输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:
输入:s = "axc", t = "ahbgdc"
输出:false

0 <= s.length <= 100
0 <= t.length <= 10^4
两个字符串都只由小写字符组成。

二、代码

【1】双指针: 初始化两个指针ij,分别指向st的初始位置。匹配成功则ij同时右移,匹配s的下一个位置,匹配失败则j右移,i不变,尝试用t的下一个字符匹配s。最终如果i移动到s的末尾,就说明st的子序列。

class Solution {
    public boolean isSubsequence(String s, String t) {
        // 定义两个双指针进行移动, 如果指针指向的位置相同,两个指针指向下一位,如果不相同,s指针移动一位
        int sIndex = 0, tIndex = 0;
        while (tIndex < t.length() && sIndex < s.length()) {
            if (t.charAt(tIndex) == s.charAt(sIndex)) {
                ++sIndex;
            }
            ++tIndex;
        }
        return sIndex == s.length();
    }
}

时间复杂度: O(n+m)其中ns的长度,mt的长度。每次无论是匹配成功还是失败,都有至少一个指针发生右移,两指针能够位移的总距离为n+m
空间复杂度: O(1)

【2】动态规划: 我们可以预处理出对于t的每一个位置,从该位置开始往后每一个字符第一次出现的位置。我们可以使用动态规划的方法实现预处理,令f[i][j]表示字符串t中从位置i开始往后字符j第一次出现的位置。在进行状态转移时,如果t中位置i的字符就是j,那么f[i][j]=i,否则j出现在位置i+1开始往后,即f[i][j]=f[i+1][j],因此我们要倒过来进行动态规划,从后往前枚举i

这样我们可以写出状态转移方程:

假定下标从0开始,那么f[i][j]中有0≤i≤m−1,对于边界状态f[m−1][..],我们置f[m][..]m,让f[m−1][..]正常进行转移。这样如果f[i][j]=m,则表示从位置i开始往后不存在字符j

这样,我们可以利用f数组,每次O(1)地跳转到下一个位置,直到位置变为ms中的每一个字符都匹配成功。

同时我们注意到,该解法中对t的处理与s无关,且预处理完成后,可以利用预处理数组的信息,线性地算出任意一个字符串s是否为t的子串。这样我们就可以解决「后续挑战」啦。

class Solution {
    public boolean isSubsequence(String s, String t) {
        int n = s.length(), m = t.length();

        int[][] f = new int[m + 1][26];
        for (int i = 0; i < 26; i++) {
            f[m][i] = m;
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = 0; j < 26; j++) {
                if (t.charAt(i) == j + 'a')
                    f[i][j] = i;
                else
                    f[i][j] = f[i + 1][j];
            }
        }
        int add = 0;
        for (int i = 0; i < n; i++) {
            if (f[add][s.charAt(i) - 'a'] == m) {
                return false;
            }
            add = f[add][s.charAt(i) - 'a'] + 1;
        }
        return true;
    }
}

时间复杂度: O(m×∣Σ∣+n),其中ns的长度,mt的长度,Σ为字符集,在本题中字符串只包含小写字母,∣Σ∣=26。预处理时间复杂度O(m),判断子序列时间复杂度O(n)

如果是计算k个平均长度为n的字符串是否为t的子序列,则时间复杂度为O(m×∣Σ∣+k×n)

空间复杂度: O(m×∣Σ∣)为动态规划数组的开销。


http://www.niftyadmin.cn/n/5088981.html

相关文章

Jenkins UI 自动化持续化集成测试

一&#xff1a;安装jenkins 环境 在官网下载msi 直接安装即可 二&#xff1a;设置全局变量 设置allure 路径 三&#xff1a;创建项目 1、创建自由风格项目 2、如果项目在本地&#xff0c;且本地服务器是windows &#xff0c;找到Jenkins安装根目录&#xff0c;寻找config…

STM32-C语言结构体地址

定义2个结构体 typedef struct _demo_node_{ //结构体本身的地址struct _demo_node_* pprenode; //实际地址开始的位置&#xff0c;最下面的输出结果可以看出struct _demo_node_* pnextnode;unsigned long member_num;unsigned short age;char addr[0]; …

ftp靶机_获取shell

ftp靶机_获取shell 文章目录 ftp靶机_获取shellftp概念实验环境信息探测 发现漏洞优化shell ftp概念 FTP 是File Transfer Protocol(文件传输协议)的英文简称&#xff0c;而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序(…

2023面试知识点二

1、vue双向绑定是如何实现的 原理主要通过数据劫持和发布订阅模式实现的通过Object.defineProperty()来劫持各个属性的setter&#xff0c;getter&#xff0c;监听数据的变化在数据变动时发布消息给订阅者(watcher)&#xff0c;订阅者触发响应的回调(update)更新视图。 2、ing…

网络安全(黑客)—自学笔记

目录 一、自学网络安全学习的误区和陷阱 二、学习网络安全的一些前期准备 三、网络安全学习路线 四、学习资料的推荐 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01; 网络安全可以基于攻击和防御视角来分类…

【LeetCode】12. 整数转罗马数字

1 问题 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000例…

openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考

文章目录 openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考99.1 参数说明99.2 认证方式 openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考 99.1 参数说明 表 1 参数说明 参数名称描述取值范…

Java中ArrayList 和 LinkedList 的区别是什么?

Java中ArrayList 和 LinkedList 的区别是什么&#xff1f; ArrayList 和 LinkedList 都是Java中常用的集合类&#xff0c;它们用于存储和操作元素的容器&#xff0c;但在内部实现和使用方式上有很大的区别。以下是它们的区别、作用、优缺点以及示例说明&#xff1a; 区别&…