【LeetCode】392.判断子序列(动态规划、双指针)

题目:

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

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

进阶:

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

示例 1:

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

示例2:

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

解法一(双指针循环):

解题思路:

题目问s是否是t的序列,是返回true不是返回false,s可以不连续的出现在t中,最直接的方法就是:用i标记s遍历的位置,用j标记t遍历的位置,一旦t中的第j个字符与s串中第i个字符相同,记录下当前j的位置,作为查找s中第i+1个字符的起点,然后i后移一位(在匹配过程中无论是否匹配成功t串都会向后移动一位,s串只有匹配成功才会后移一位),如此,直至遍历完t串。如果s串比t串先遍历完的,那么说明s串为t串的子序列,返回true;如果因为遍历完t串而跳出遍历t串的内循环,也就是说从起点找到最后了都没有找到,s串的第i个字符,那么就返回false。两冲循环解决效率也是非常高的。

代码部分:

//C语言代码:
bool isSubsequence(char * s, char * t){
    int step = 0,j;//step记录s串每一个字符进行匹配时遍历t串的起点;
    for(int i = 0; s[i] != '\0'; i++)//遍历s串;
    {
        for(j = step; t[j] != '\0'; j++)//遍历t串;
        {
            if(t[j] == s[i])//成功匹配;
            {
                step = j+1;//记录位置作为s串下一个字符的遍历起点;
                break;//跳出循环;
            }
        }
        if(step != j+1)//如果不是因为成功匹配而跳出内循环,s不是t的子序列,返回false;
          return false;
    }
    return true;//s优先t遍历结束,s是t的子序列返回true;
}

运行效率:

解法二:

解题思路(动态规划):

还是和往常一样,找到操作和状态,这道题不是一个实际问题,我们把它看成实际问题,如果让你现实生活中去完成这件事,你的操作是什么就是拿着s串的一个字符去和t串一个一个匹配,那么操作一个一个字符的匹配,状态又是什么呢?我们要完成匹配就是要看在找完一遍t串的时候s串完成匹配的字符个数和s串的长度是否相等,那么状态就是完成s串匹配的字符数量。我们可以建立一个二维数组dp[i][j]来记录在s串遍历到i,t串遍历到j时已经完成匹配的字符数(遍历到i和j时,实际匹配的是,s[i-1]和t[j-1],这个已经很关键,后面会一直用到)。

为了实现记录已经完成的匹配字符数,需要记录s的每一位i对应t的每一位j已经完成匹配的字符数,所以需要s中的每一个字符找到遍历到t的每一个字符时最多可以匹配的字符数。

当s串的第i位和t串的第j位,检测是否需要更新完成匹配的字符数。

如果需要,也就是说s[i-1] == t[i-1]此时的dp[i][j]就等于当s串遍历到i-1,t遍历到j-1时已经完成匹配(不包含s[i-1],t[j-1])的字符数+1。状态转移方程为:

dp[i][j] = dp[i-1][j-1]+1; 

如果不需要,此时dp[i][j]就等于当s串遍历到第i位,t串遍历到第j-1位时已经完成匹配的字符数,也就是说这一次匹配s[i-1]和t[i-1]与上一次s[i-1]与t[j-2]的匹配说没有差别,状态转移方程:

dp[i][j] = dp[i][j-1]

注意:上面的数组的下标出现了i-1和j-1,数组的下标要大于0,所以遍历要从就j=1和i=1开始。

最后dp[i-1][j-1] 时候为s串的长度,如果是那么返回true,不是则返回false。

代码部分:

//C语言代码;
bool isSubsequence(char * s, char * t){
    int dp[105][10005] = {0},i,j = 1;
    for(i = 1; s[i-1] != '\0'; i++)//遍历s串;
    {
        for(j = 1; t[j-1] != '\0'; j++)//遍历t串;
        {
            if(t[j-1] == s[i-1])
            {
                dp[i][j] = dp[i-1][j-1]+1; //状态转移,找到在s第i位,t第j位已经完成的字符串匹配个数;
            }
            else
            {
                dp[i][j] = dp[i][j-1];//状态转移,与s第i位,t第j-1位已经完成的字符串匹配个数相同
            }
        }
    }
    if(dp[i-1][j-1] != i-1)//dp的右下角下标位[i-1][j-1],s字符长是i-1;
       return false;
    return true;
}

运行效率:

 不得不说这段代码性能很差.......

ps:其实最好理解的方法还是画图!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值