力扣刷题笔记:最长子串、子序列(10)

3. 最长无重复字符的子串(滑动窗口)

1、使用滑动窗口法,首先尾指针加1,表示加入一个元素,然后更新最长子串长度。
2、判断增加元素后原窗口是否有重复元素,找到第一个重复元素,更改首指针到该元素后。

    int lengthOfLongestSubstring(string s) {
		int start = 0, end = 0, length = 0, num = 0;
		while (end < s.size()){
			end++;length++;
			num = max(num, length);
			for(int n=start;n<end;n++)
				if (s[n] == s[end]){
					start = n + 1;
					length = end - start;
					break;
				}
		}
		return num;
    }

5. 最长回文子串(双重遍历+二维动规)

1、二维dp数组保存该区间是否是回文子串
2、长度l从0开始,长度为1,则dp为1,长度为2,判断i,j是否相等;长度大于2,判断是否相等且i+1,j-1是否为1
3、用string实时记录最长子串,并实时更新

class Solution {
public:
    string longestPalindrome(string s) {
	int n = s.size();
		string sub;
		//用vector,定义二维数组,存动态规划状态量,小括号内为初始化
		vector<vector<int>> dp(n, vector<int>(n));
		for (int l = 0; l < n; l++)		{
			//注意加上边界判断!官方代码运行会报错
			for (int i = 0; i+l < n; i++)			{
				int j = i + l;
				if (l == 0)
					dp[i][i] = 1;
				else if (l == 1 && s[i] == s[j])
					dp[i][j] = 1;
				//动规判断比i,j小的,向较短方向转移,而非较长转移,注意顺序
				else if (dp[i+1][j-1]==1 && s[i] == s[j])
					dp[i][j] = 1;
				//大于当前最大子串长度
				if (dp[i][j] == 1 && l + 1 > sub.size())
				//注意substr第二项,并非序号,应为从i往后第l+1个
					sub = s.substr(i, l+1);
			}
		}
		return sub;
    }
};

76. 最小覆盖子串(滑动窗口)

1、用滑动窗口控制一个字符串范围,如果不能覆盖,一直移动右窗口,如果可以覆盖,往右收缩左窗口
2、用map记录当前子串是否可以覆盖,由于给定字符串可能重复,所以要记录个数

class Solution {
public:
    unordered_map <char, int> ori, cnt;
    bool check() {
        for (const auto &p: ori) 
            if (cnt[p.first] < p.second) 
                return false;
        return true;
    }
    string minWindow(string s, string t) {
        for (const auto &c: t) 
            ++ori[c];
        int l = 0, r = -1,len = INT_MAX, ansL = -1, ansR = -1;
        while (r < int(s.size())) {
            if (ori.find(s[++r]) != ori.end()) 
                ++cnt[s[r]];
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1;
                    ansL = l;
                }
                if (ori.find(s[l]) != ori.end()) 
                    --cnt[s[l]];
                ++l;
            }
        }
        return ansL == -1 ? string() : s.substr(ansL, len);
    }
};

718. 最长重复子数组(双重遍历+二维动规)NC127 最长公共子串

1、与最长公共子序列不同,子串要求必须连续,如果不相等dp等于0
2、用二维数组dp记录当前两个数组下标的已有最长子串,从头开始遍历
3、如果相等,则等于之前子串加1,dp[i+1][j+1]=dp[i][j]+1;如果不等,则dp[i+1][j+1]=0

    int findLength(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size(),m=nums2.size(),max=0;
        vector<vector<int>> dp(n+1,vector<int>(m+1,0));
        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(nums1[i]==nums2[j])
                dp[i+1][j+1]=dp[i][j]+1; 
            else
                dp[i+1][j+1]=0;  
            if(dp[i+1][j+1]>max)
                max=dp[i+1][j+1];      
        }
        return max;
    }

14. 最长公共前缀(排序+比较)

1、正常方法是所有字符从第一个字符开始从前往后挨个判断
2、取巧方法对字符串数组排序,直接比较第一个和最后一个即可,因为第一个和最后一个一定相差最大

    string longestCommonPrefix(vector<string>& strs) {
        if(strs.empty()) return string();
        sort(strs.begin(), strs.end());
        string st = strs.front(), en = strs.back();
        int i, num = min(st.size(), en.size());
        for(i = 0; i < num && st[i] == en[i]; i ++);
        return string(st, 0, i);
    }

53. 最大子序和(一次遍历+一维动规)

1、动态规划,用数组num当dp数组,nums[i]表示以i结尾最大子序和
2、如果num[i-1]大于0,则加上,小于0,不加,用ans记录dp中当前最大值

    int maxSubArray(vector<int>& nums) {
    int ans=nums[0];
    for(long i=1;i<nums.size();i++){
        if(nums[i-1]>0)
            nums[i]+=nums[i-1];
        ans=max(ans,nums[i]);
    }
    	return ans;
    }

128. 最长连续序列(哈希表)

1、这个连续不是指在原数组中连续,而是指数字大小连续,所以可以使用哈希表来去重和顺序查找
2、为了减少复杂度,查找前先判断n-1存不存在,不存在说明该点是一个序列的起点,可以查找

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> num_set;
        for (const int& num : nums) 
            num_set.insert(num);
        int longestStreak = 0;
        for (const int& num : num_set) {
            if (!num_set.count(num - 1)) {
                int currentNum = num;
                int currentStreak = 1;
                while (num_set.count(currentNum + 1)) {
                    currentNum += 1;
                    currentStreak += 1;
                }
                longestStreak = max(longestStreak, currentStreak);
            }
        }
        return longestStreak;           
    }
};

300. 最长递增子序列(两层遍历+一维动规)

1、由于是子序列问题,不是子数组,所以可以分开,所以不能连续一遍动规,必须两层动规扫描
2、在每个点的时候都从头再扫一遍,到这个的最长子序列,保存最大值

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = (int)nums.size();
        if (n == 0) 
            return 0;
        vector<int> dp(n, 0);
        for (int i = 0; i < n; ++i) {
            dp[i] = 1;
            for (int j = 0; j < i; ++j) {
                if (nums[j] < nums[i]) 
                    dp[i] = max(dp[i], dp[j] + 1);
            }
        }
        return *max_element(dp.begin(), dp.end());
    }
};

674. 最长连续递增序列(贪心)

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int ans = 0;
        int n = nums.size();
        int start = 0;
        for (int i = 0; i < n; i++) {
            if (i > 0 && nums[i] <= nums[i - 1]) {
                start = i;
            }
            ans = max(ans, i - start + 1);
        }
        return ans;
    }
};

1143. 最长公共子序列(一次遍历+二维动规)

1、与最长公共子串不同,子序列不要求连续,如果不相等则dp等于之前较大的一个
2、用二维数组dp记录当前两个数组下标的已有最长子序列,从头开始遍历
3、如果相等,则等于之前子串加1,dp[i+1][j+1]=dp[i][j]+1;如果不等,则dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1])

    int longestCommonSubsequence(string text1, string text2) {
        int m=text1.size(),n=text2.size(),ans=0;
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        for(int i=0;i<m;i++)
        for(int j=0;j<n;j++)
        {
            if(text1[i]==text2[j])      
                dp[i+1][j+1]=dp[i][j]+1;
            else
                dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
            ans=max(ans,dp[i+1][j+1]);
        }
        return ans;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值