Leetcode 392. 判断子序列——Python实现

Leetcode 392. 判断子序列——Python实现

题目描述

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

此处,s的长度不超过100, t的长度不超过10000,这两个字符串都只会有小写组成。

举例1:
输入:s = “abc”, t = “ahbgdc”
输出:true

举例2:
输入:s = “aaaaaa”, t = “bbaaaa”
输出:false

举例3:
输入:s = “afc”, t = “ahbgjc”
输出:false

解法一:使用索引进行比对
思路分析:主要是通过索引获取字符串片段进行查找比对。
       首先判断字符串s和t是否为空,为空就返回false。不为空,就遍历s中的每个字符是否在在字符串t中,只要有一个字符串不在t中,那么就返回false。需要注意的是,为比避免s中重复的字符串出现在t中只会匹配到其第一个的位置,因此t的字符串需要不断减小,需要减去前面匹配过的部分,这样才符合子串的定义

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        if s and t:  # 如果s或中有空字符串,立即返回false
            return False
        else:
	        N = len(t)  # t字符串的总长度
	        i = 0   # 表字符串开始匹配的位置
	        for j in s:  # 遍历s中每个字符
	            if i >= N:  # 字符匹配的位置不能超过子串的总长度
	                return False
	            if i not in t[i:j]:  # 判断s中的字符是否在t中
	                return False
	            else:
	                start = t.index(j,i,N) + 1  # 在t[j:N]字符串的中查找是有s的字符后立马向后查询
	        return True

解法二: 使用双指针求解
思路分析:对于字符串s和字符串t各自设置一个指针i,j,从各自的首尾元素出发,然后进行比较,如果对应的元素相同,那么两个指针都向后移动一位,如果不相同,那么t的指针向后移动一位,直到遍历完s的所有元素。最后s的指针一定移动到了最后一个元素,此时的指针对应的数值如果与s的长度相等,那么返回true,否则返回false

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
	    if s and t:	 # 如果s或中有空字符串,立即返回false
	    	return False
	    else:
	        i = j = 0  # 设置两个指针,i指向s中的元素,j指向t中的元素,从首位开始
	        M = len(s)
	        N = len(t)
	        while i < M and j < N:  # 当这两个指针都,没有超过各自的字符串长度时
	            if s[i] == t[j]:  # 如果字符串s中的元素出现在字符串t中,那么i,j各自向后移动一位
	                i += 1
	            j += 1   # 如果字符串s中的元素没有出现在字符串t中,那么j向后移动一位
	        return(i == len(s))  # 只有当指针i最终的位置与字符串s的长度相等时,才返回true

解法三:动态规划方法
使用动态规划的方法对要比对的字符串t进行处理,令 dp[i][j]表示字符串t中从位置i开始往后字符 j 第一次出现的位置。在进行状态转移时,如果t 中位置i的字符就是j,那么dp[i][j]=i,否则j 出现在位置i+1 开始往后,即 dp[i][j]=dp[i+1][j],因此需要倒过来进行动态规划,从后往前枚举i。
这样我们可以写出状态转移方程:
d p [ i ] [ j ] = { i t[i]=j d p [ i + 1 ] [ j ] t[i]!=j dp[i][j]= \begin{cases} i& \text{t[i]=j}\\[2ex] dp[i+1][j]& \text{t[i]!=j}\\[2ex] \end{cases} dp[i][j]= idp[i+1][j]t[i]=jt[i]!=j
假定下标从 0 开始,那么dp[i]][j] 中有$ 0 ≤i≤M−1$ ,对于边界状态 dp[M-1][…],我们置 dp[M][…]为 M,让 dp[M-1][…] 正常进行转移。这样如果 dp[i][j]=m,则表示从位置 i开始往后不存在字符j。

class Solution:
    def isSubsequence2(self, s: str, t: str) -> bool:
        if s and t:  # 如果s或中有空字符串
            return False
        else:
            N, M = len(s), len(t)
            dp = [[0] * 26 for _ in range(M)]  # 将字符串t的每个元素设置为打表器,行数为t的元素下标,列数为26个字母A-Z
            dp.append([M] * 26)  # 多设置一列,用M表示正无穷,也就是不存在

            for i in range(M-1, -1, -1, -1):
                for j in range(26):
                    if ord(t[j] == j +ord('a')):
                        dp[i][j] = i
                    else:
                        dp[i][j] = dp[i+1][j]

            add = 0
            for i in range(N):
                if[add][ord(s[i]) - ord('a')] == M:
                    return False
                add = dp[add][ord(s[i]) - ord('a')] + 1

            return True

在字符串s较少时,使用双指针法更加节省时间,如果有大量的s需要查找,那么动态规划更加节省时间。因为双指针方法每次都需要遍历字符串t,而动态规划只需要一次求出打印表即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值