子序列与子串问题


按照顺序看。

最长公共子序列

在这里插入图片描述

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {

        vector<vector<int>> dp(text1.size()+1,vector<int>(text2.size()+1,0));

        for(int i=1;i<=text1.size();i++)
        {
            for(int j=1;j<=text2.size();j++)
            {
                if(text1[i-1]!=text2[j-1])
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
                else
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
            }
            
        }
        return dp[text1.size()][text2.size()];
    }
};

上面是返回长度,牛客上还有一道要求返回最长的那个子串。

class Solution {
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
        int n=s1.size();
        int m=s2.size();
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=m;++j)
            {
                if(s1[i-1]==s2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }              
            }
        }
        int i=s1.size();
        int j=s2.size();
        string ret;
        while(i > 0 && j > 0)
        {
            //从后往前找,如果相等就--继续找
               if(s1[i-1]==s2[j-1])
               {
                   ret+=s1[i-1];
                   i--;
                   j--;
               }//如果不相等,看自己是由那个推过来的
               else if(dp[i-1][j]>dp[i][j-1])
               {
                  i--;
               }
               else
               {
                   j--;
               }   
        }
        reverse(ret.begin(),ret.end());
        return ret.size()==0?"-1":ret;
    }
};

最长连续递增子序列

在这里插入图片描述

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        if(nums.size()<=1)
        return nums.size();
        
         vector<int> dp(nums.size(),1);
                 
        
         int maxL=INT_MIN;
         for(int i=1;i< nums.size();i++)
         {
                 if(nums[i]>nums[i-1])
                 {
                     dp[i]=dp[i-1]+1;
                 }
                 maxL=dp[i]>maxL?dp[i]:maxL;

         }
         return maxL;
    }
};

最长递增序列

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1)
        {
            return nums.size();
        }
        //下标为i时,第i个递增子序列的长度

        //为1个的时候可以看做递增,初始化为1
        vector<int> dp(nums.size(),1);

        int maxVal=INT_MIN;
        //自己不和自己比
        for(int i=1;i<nums.size();i++)
        {
            for(int j=0;j<i;j++)
            {
                if(nums[i]>nums[j])
                {
                dp[i]=max(dp[i],dp[j]+1);
                }
                 maxVal=dp[i]>maxVal?dp[i]:maxVal;
            }
        }
        return maxVal;
        
    }
};

最长连续序列

和无重复字符的最长子串相对照
在这里插入图片描述

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
            unordered_set<int> hash(nums.begin(),nums.end());
           int maxL=0;
           while(!hash.empty())
           {

                int now=*hash.begin();//找到开始  
                hash.erase(now);//删除他
                int l=now-1;
                int r=now+1;
                   while(hash.find(l)!=hash.end())
                   {
                       //--去找比他小的
                       hash.erase(l);
                      l--;
                   }

                   while(hash.find(r)!=hash.end())
                   {
                       //++去找比他大的
                       hash.erase(r);
                       r++;
                   }
                   maxL=max(maxL,r-l-1);
               }

           return maxL;
    }
};

判断子序列

普通解法

可以转换成求他们两个的最长公共子序列,然后看长度是否和s一样,一样就说明T中有完整的s.

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n=s.size();
        int m=t.size();
         vector<vector<int>> dp(n+1,vector<int>(m+1,0));

         for(int i=1;i<=n;++i)
         {
             for(int j=1;j<=m;++j)
             {
                 if(s[i-1]==t[j-1])
                 {
                     dp[i][j]=dp[i-1][j-1]+1;
                 }
                 else
                 {
                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                 }
             }
         }

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

优化

双指针,当他们两个相等的时候同时向后走,不相等只有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;
    }
};

不同的子序列

上一遍博客

无重复字符的最长子串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
           int n=s.size();
           int i=0;//滑动窗口起始值
           
           int hash[256]={0};//有空格所以不能映射26个字符的相对位置
           int result=0;
           for(int j=0;j<n;++j)
           {
              hash[s[j]]++;
              while(hash[s[j]]==2)
              {
                  hash[s[i]]--;
                  i++;
              }
             result=max(result,j-i+1);
           }
        return result;
    }
};

最长重复数组

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size();
        int m=nums2.size();
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        
        int maxL=0;
        for(int i=1;i<=n;++i)
        {
          for(int j=1;j<=m;++j)
          {
            if(nums1[i-1]==nums2[j-1])
            {
                dp[i][j]=dp[i-1][j-1]+1;
            }
            maxL=max(maxL,dp[i][j]);
          }
        }

        return maxL;
    }
};

最长公共子串

和子序列不同的是,子串要求连续。牛客这道题也是要求返回最长的子串

class Solution {
public:
    /**
     * longest common substring
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    string LCS(string str1, string str2) {
        int n=str1.size();
        int m=str2.size();
        
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        
        int maxL=0;
        int maxi=0;
         for(int i=1;i<=n;++i)
        {
          for(int j=1;j<=m;++j)
          {
              if(str1[i-1]==str2[j-1])
              {
                  dp[i][j]=dp[i-1][j-1]+1;
              }
              
              if(dp[i][j]>maxL)
              {
                maxL=dp[i][j];
                maxi=i-1;
              }
          }
        }
        
        return str1.substr(maxi-maxL+1,maxL);
    }
};

两个字符串的删除操作

第一种方法

dp[i][j]为,以第i个元素结尾的word1,和以底j个元素结尾的word2,要达到相等,所需要删除的最小次数.

这个是两个字符串你都可以删除。开始初始化为可能操作的最大次数。即i。
然后当两个字符相等,不用删除,他就等于上一次的操作次数。
假如不相等
在这里插入图片描述

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n=word1.size();
        int m=word2.size();
       vector<vector<int>> dp(n+1,vector<int>(m+1,0));
          for(int i=0;i<=n;++i)
          {
              dp[i][0]=i;
          }
          for(int j=0;j<=m;++j)
           {
            dp[0][j]=j;   
           }
        
       for(int i=1;i<=n;++i)
       {
           for(int j=1;j<=m;++j)
           {
               if(word1[i-1]==word2[j-1])
               {
                   dp[i][j]=dp[i-1][j-1];
               }
               else
               {
                   //不相等的时候,word1删一个,word2删一个,看哪个大
                   dp[i][j]=min(dp[i-1][j]+1,dp[i][j-1]+1);
                   //也可以直接各删一个,求他们三个的最小
                   dp[i][j]=min(dp[i][j],dp[i-1][j-1]+2);
               }
           }
       }
       return dp[n][m];
    }
};

第二种方法

求出最长公共子序列长度,然后将两个字符串的长度加起来,减去2*最长长度。

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n=word1.size();
        int m=word2.size();
         vector<vector<int>> dp(n+1,vector<int>(m+1,0));

         for(int i=1;i<=n;++i)
         {
             for(int j=1;j<=m;++j)
             {
                 if(word1[i-1]==word2[j-1])
                 {
                 //上一个最长的长度+1
                     dp[i][j]=dp[i-1][j-1]+1;
                 }
                 else
                 {
                     dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                 }
             }
         }
         return m+n-2*dp[n][m];


    }
};

编辑距离

判断子序列,不同的子序列,两个字符串的删除操作。这三道题的进阶,具体讲解可以看上一遍博客

回文子串

class Solution {

public:
    int countSubstrings(string s) {

      int n=s.size(); 
      vector<vector<bool>> dp(n,vector<bool>(n,false));     
      for(int i=n-1;i>=0;--i)
       {
           for(int j=i;j<n;++j)
           {
               if(i==j)
               {
                   dp[i][j]=true;
               }
               else if(i==j-1)
               {
                   dp[i][j]=(s[i]==s[j]);
               }
               else
               {
                   dp[i][j]=(dp[i+1][j-1])&&(s[i]==s[j]);
               }
           }
       }
       int count=0;
       for(int i=0;i<n;++i)
       {
           for(int j=0;j<n;++j)
           {
               if(dp[i][j])
               {
                   ++count;
               }
           }
       }
       return count;
    }
};

最长回文子串

class Solution {
public:
    string longestPalindrome(string s) {
        int n=s.size();
       vector<vector<bool>> dp(n,vector<bool>(n,false));
     
       int maxLength=INT_MIN;
       //等会做为sustr的参数
       int begin=0;
       for(int i=n-1;i>=0;--i)
       {
           for(int j=i;j<n;++j)
           {
               if(i==j)
               {
                   dp[i][j]=true;
               }
               else if(i==j-1)
               {
                   dp[i][j]=(s[i]==s[j]);
               }
               else
               {
                   dp[i][j]=(s[i]==s[j])&&(dp[i+1][j-1]);
               }
               if(dp[i][j] && j-i+1>maxLength)
               {
                   maxLength=j-i+1;
                   begin=i;
               }
           }
       }
       return s.substr(begin,maxLength);
       }
};

最长回文序列

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值