力扣判断子序列的俩种算法

问题描述:

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

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

示例:

分析:

该算法很容易想到运用双指针分别指向s和t字符串的第一个字符,如果俩指针指向的字符相同,都往后移动一个单位,如果不同,说明没有匹配成功,那么指向s的指针不动,而指向t的指针往后移。流程图如下:

 双指针代码:

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n=s.size();
        int m=t.size();
        int i=0;
        int j=0;
        while(i<n&&j<m){
            if(s[i]==t[j]){
                i++;
            }
            j++;
        }
        return i==n;
    }
};

注意到如果我们需要检查非常多的s字符串是否为t的子字符串,那么双指针的算法就不太高效了。所以我们考虑动态规划的方法。

动态规划:

我们可以定义一个二维表dp[i][j]用来表示t字符串从位置i开始往后第j个字母第一次出现的位置,行代表t字符串第i位的字母,列代表26个字母。我们可以通过一个例子来理解。

例:s:"abz"                t:"aazbdz"

 从后往前遍历t字符串,首先最后一个字母为z,我们检查dp表发现在z后没有其他字母出现,所以我们将dp[5][0~25]都与dp[6][0~25]保持一致(将第六行都初始化为6,因为6已经超过t字符串下标上限,所以可以认为是无穷大,代表t[i]之后没有出现第j个字母),而z第一次出现的位置就是i,所以更新二维表可得上面右图所示。以此类推可以得到最终dp为:

 之后我们将s与dp开始比对,s[0]='a',在dp表0行查询得到t字符串中'a'第一次出现的位置是t[0],

因此我们从第一次出现该字母的下一个位置比对s[1],也就是dp表的1行。

s[1]与dp表1行比对得到'b'在t字符串中第一次出现的位置是t[3],所以同理从第一次出现的位置+1的位置比对下一个字母s[2],以此类推。

 

 只要发现s[i]在dp表中相应检查行中为6,则说明该字母在t的剩余字符串中未出现过,我们就返回false。

动态规划代码:

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n=s.size(),m=t.size();
        vector<vector<int>> dp(m+1,vector<int>(26,0));  //定义一个动态规划二维表,行代表t字符串,列代表26个字母
        for(int i=0;i<26;i++)   //初始化第m行值为相对意义上的无穷(下标超过t字符串最大下标)
            dp[m][i]=m;
        for(int i=m-1;i>=0;i--){    //从t字符串后往前记录i位置第j个字母第一次出现的位置
            for(int j=0;j<26;j++){
                if(t[i]==j+'a'){    //出现了则记录下当前位置
                    dp[i][j]=i; 
                }
                else{               //未出现则将上一组的状态写入当前状态中
                    dp[i][j]=dp[i+1][j];
                }
            }
        }
        int k=0;
        for(int i=0;i<n;i++){       //检查s字符串
            if(dp[k][s[i]-'a']==m)  //遍历发现没有出现过s[i]字符则返回false
                return false;
            k=dp[k][s[i]-'a']+1;    //否则取该字母第一次出现的位置,并且从其在t字符串第一次出现的位置的下一个位置开始检查s字符串下一个字符
        }
        return true;    //依次遍历完后都能在dp表中查到相应的位置即返回true
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值