LeetCode-子序列-字符串序列

1 序列解题思路

        序列的特点是不连续,序列相关的题目主要是子序列和字符串序列。这两类题目主要使用动态规划来解决。

        对比子数组和子字符串的题目:LeetCode-子数组-子字符串(也就是连续的序列)_hclbeloved的博客-CSDN博客

2 子序列相关题目

2.1 最长递增子序列

300. 最长递增子序列

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size(), maxLen = 0;
        if (n == 0) 
        {
            return 0;
        }
        //dp[i]: 以索引 i 处数字结尾的最长上升子序列的长度
        vector<int> dp(n, 1);
        for (int i = 0; i < n; ++i) 
        {
            for (int j = 0; j < i; ++j) 
            {
                if (nums[i] > nums[j]) 
                {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }

            maxLen = std::max(maxLen, dp[i]);
        }

        return maxLen;
    }
};

2.2 最长连续序列

128. 最长连续序列

剑指 Offer II 119. 最长连续序列

        注意这里的连续指的是数字大小的连续性,并不是数组中数据前后的连续性,所以将这个题目放到了这里。

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        //方法一:最小堆,不满足O(n),实际上是o(nlogn)
        // priority_queue<int, vector<int>, greater<int>> minHeap;
        // unordered_set<int> s;
        // int longest = 0;
        // for(auto& i : nums)
        // {
        //     if (!s.count(i))
        //     {
        //         s.insert(i);
        //         minHeap.push(i);
        //     }            
        // }

        // while(!minHeap.empty())
        // {
        //     int start = minHeap.top(), wanted = start+1, count = 1;
        //     minHeap.pop();
        //     while(minHeap.top() == wanted)
        //     {
        //         ++wanted;
        //         ++count;
        //         minHeap.pop();
        //     }

        //     longest = std::max(longest, count);
        // }

        // return longest;

        // 方法二: 满足O(n)
        unordered_set<int> s;      
        for(auto& i : nums)
            s.insert(i);

        int longest = 0;
        for (auto& i : s)
        {
            if (!s.count(i-1))
            {
                int start = i, wanted = start+1, count = 1;
                while (s.count(wanted))
                {
                    ++wanted;
                    ++count;
                }

                longest = std::max(longest, count);
            }
        }

        return longest;
    }
};

2.3

2.4

2.5

3 字符串序列相关题目

3.1 最长公共子序列

剑指 Offer II 095. 最长公共子序列

1143. 最长公共子序列

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        //从底到上的动态规划
        int n = text1.length(), m = text2.length();
        if (0 == n*m)
            return 0;

        //dp[i][j]: text1[0...i-1]与text2[0...j-1]的最长公共子序列的长度
        //dp[0][j] 表示text1[0...-1]与text2[0...j-1]的最长公共子序列的长度,可理解为text1此时字符串长度为0;
        //dp[i][0] 有类似的含义
        //或者这样理解,dp[i][j]表示text1前i个元素与text2的前j个元素的最长公共子序列的长度
        vector<vector<int>> dp(n+1, vector<int>(m+1,0));
        //base case
        for (int i=0;i<=n;++i)
            dp[i][0] = 0;

        for (int i=0;i<=m;++i)
            dp[0][i] = 0;

        //状态转移方程
        for(int i=1;i<=n;++i)
        {
            for (int j=1;j<=m;++j)
            {
                if (text1[i-1] == text2[j-1])
                {
                    dp[i][j] = 1 + dp[i-1][j-1];
                }
                else
                {
                    dp[i][j] = std::max(dp[i-1][j], dp[i][j-1]);
                }
            }
        }

        return dp[n][m];
    }
};

3.2 子序列的数目

剑指 Offer II 097. 子序列的数目

115. 不同的子序列

class Solution {
public:
    //dp[i][j]表示 t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数
    vector<vector<int>> dp;

    int numDistinct(string s, string t) {
        //方法三:从上到下(将大问题转化为小问题的递归求解)的动态规划
        int n = s.length(), m = t.length();
        if (n < m)
            return 0;
        else if (n == m)
            return s==t ? 1 : 0; 

        if (m == 0)
            return 1;  

        if (dp.empty())
            dp.assign(n+1, vector<int>(m+1, -1));

        if (-1 != dp[n][m])
            return dp[n][m];

        //从后向前在“源串”中查找第一个等于“模式串”最后一个字符的索引
        for (int j=n-1;j>=0;--j)
        {
            if (s[j] != t[m-1])
                continue;
            // dp[i][j]表示 t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数
            //状态转移方程:当 s[n-1] == t[m-1] 时,dp[n][m] = dp[n-1][m-1] + dp[n-1][m]
            dp[n][m] = numDistinct(s.substr(0,j), t.substr(0,m-1)) + numDistinct(s.substr(0,j), t);
            break;//很有必要,for循环的目的是找到s[j] == t[m-1]
        }
        
        // dp[n][m] = -1,说明上面的for循环没有执行到 dp[n][m] = numDistinct(s.substr(0,j), t.substr(0,m-1)) + numDistinct(s.substr(0,j), t);
        // 就说明此时 “t的前j个元素构成的字符串 在 s的前i个元素构成的字符串 中出现的个数为0”
        dp[n][m] = (dp[n][m] == -1 ? 0 : dp[n][m]);
        return dp[n][m];
    }
};

3.3

3.4

3.5

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值