【C++】字符串子串的系列问题

  • Post author:
  • Post category:其他




一、解题背景:

字符串的子串应该是常见的一类有关字符串的算法题目,这里我将leetcode的相关几道题汇总了一下,写了一些具体的思路和多种的求解方法。



二、问题求解:



3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = “abcabcbb”

输出: 3

解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。

/***********************************************************************/

输入: s = “bbbbb”

输出: 1

解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。

class Solution {
public:
	// 方法1:
    int lengthOfLongestSubstring1(string s) {
	    unordered_map<char,int> mp; // 记录的是索引位置
	    int ans = 0, i = 0;
	    for (int j=0; j<s.size(); j++) {
	    	if (mp.find(s[j])!=mp.end()) {
				i = max(mp[s[j]], i);
			}
			ans = max(ans, j-i+1);
			mp[s[j]] = j+1; // 保存对应字符后边的位置索引,可能作为下一次无重复字符串起点
		}
		return ans;
	}
	// 方法2:
	int lengthOfLongestSubstring1(string s) {
		unordered_map<char,int> mp; // 记录的是字符出现的次数
		int ans = 0;
		for (int i=0, j=0; j<s.size(); j++) {
			mp[s[j]]++;
			while (mp[s[j]]>1) { // 同样这个步骤也是为了找到相同字符的后一个位置
				mp[s[i++]]--;
			}
			ans = max(ans, j-i+1);
		}
		return ans;
	}
};



159. 至多包含两个不同字符的最长子串

给定字符串 s,判断最长的只含有一个或者两个字符的子串的长度。

input_str = “kkkk”

answer = 4

解释:最长子串是 kkkk。

/***********************************************************************/

input_str = “abdyd”

answer = 3

解释:满足条件的最长子串是 dyd。

class Solution {
public:
	bool cmp(const pair<char, int> left,const pair<char, int> right) {
		return left.second<right.second;
	}
    int lengthOfLongest2DisSubstring(string s) {
  		int ans = 1;
  		unordered_map<char, int> mp;
  		for (int j=0, i=0; j<s.size();j++) {
			mp[s[j]] = j;
			
			if(mp.size()==3) {
				auto iter = min_element(mp.begin(), mp.end(), cmp); // 找到最小的value的键值对
				i = iter->second+1;
				mp.erase(iter);
			}
			ans = max(ans, j-i+1);
		}
		return ans;
	}  
};



395. 至少有 K 个重复字符的最长子串

给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。

实例1:

输入:s = “aaabb”, k = 3

输出:3

解释:最长子串为 “aaa” ,其中 ‘a’ 重复了 3 次。

实例2:

输入:s = “ababbc”, k = 2

输出:5

解释:最长子串为 “ababb” ,其中 ‘a’ 重复了 2 次, ‘b’ 重复了 3 次。

class Solution {
public:
	// 这里主要采用了分治的策略,从而使得问题拆分小,这里的核心是将整个字符串中次数未超过k次的作为分隔符来进行计算出现次数至少k次的最长子串。
	int dfs(string &s, int l, int r, int k, int res=0) {
		if (l>r) return 0;
		unordered_map<char,int> mp;
		for (int i=l; i<=r; i++)
			mp[s[i]]++;
		vector<int> pos;
		pos.push_back(l-1);
		for (int i=l; i<=r; i++)
			if (mp[s[i]]<k) pos.push_back(i);
		pos.push_back(r+1);
		if (pos.size()==2) return r-l+1;
		for (int i=0; i<pos.size()-1; i++) {
			res = max(res, dfs(s, pos[i]+1, pos[i+1]-1, k, res));
		}
		return res;
	}
	int longestSubstring(string s, int k) {
	    if (s.size()==0 || s.size()<k) return 0;
	    if (k<=1) return s.size();
	    return dfs(s,0,s.size()-1,k,0);
	}
};

参考的思路:来源leetcode题解

//分治:对于一个字符串来说,如果要求子串最少出现k次,那么如果某些字母出现的次数小于k,
//这些字母一定不会出现在最长的子串中,并且这些字母将整个字符子串分割成小段,这些小段有可能是最长的
//但是由于被分割了,还是要检查这一小段,如果某些字母出现的次数小于k,会将小段继续分割下去,
//比如字符串"aacbbbdc",要求最少出现2次,我们记录左右闭区间,,
//第一轮[0,7],处理"aacbbbdc",d只出现了一次不满足,于是递归解决区间[0,5]、[7,7]
//第二轮[0,5],处理"aacbbb",  c只出现了一次不满足,于是递归解决区间[0,1]、[3,4] 
//第二轮[7,7],处理"c",       c只出现了一次不满足,不继续递归
//第三轮[0,1],处理"aa",      满足出现次数>=2,res=2
//第三轮[3,4],处理"bbb",     满足出现次数>=2 res=3;



版权声明:本文为qq_42346574原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。