【Leetcode】395. Longest Substring with At Least K Repeating Characters

题目链接:https://leetcode.com/problems/longest-substring-with-at-least-k-repeating-characters/

题目:

Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times.

Example 1:

Input:
s = "aaabb", k = 3

Output:
3

The longest substring is "aaa", as 'a' is repeated 3 times.

Example 2:

Input:
s = "ababbc", k = 2

Output:
5

The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.

思路:

1.先从头开始扫描,遇到第一个满足条件的子串,则用set 记录该子串的字符种类,并记录该子串的长度。此时字符串已经被分割成了一段一段的,设某段结尾字符的下标为 i ,则sets[i]表示以i为结尾的子串的字符集,len[i]表示该串的长度。此时set数组的每个set元素有三种状态,一个为null 表示该字符处于某有效字符串中,set长度为0表示从该字符开始的子串没有符合条件的,所以set为0,可以当成分隔符号,set长度大于0,则以当前字符为结尾的存在有效子串。

2. 下一步考虑合并这些一段一段的子串或分隔符,合并的时候不仅需要知道前子串的字符集 还要知道前段子串的长度 所以我设置了set、len数组。  扫描set数组,若当前set为0 则判断该字符是否和前一段构成子串;若当前set不为0 则将该子串和前一段子串或分隔符直接连接起来。

时间开销主要在分割子串上,时间复杂度为O(n^2),空间复杂度为O(n)。代码可以更进一步简化。

算法:

	public int longestSubstring(String s, int k) {
		if (s.length() < k)
			return 0;
		int max = 0;
		HashSet[] sets = new HashSet[s.length()];
		int len[] = new int[s.length()];// 子集合的长度
		// 有三种状态:为null表示当前字符在某个符合条件的子串中;
		// size为0的set表示当前字符不能和任何之后的子串表示一个符合要求的子串
		// size不为0,表示之前某字符开始到当前字符为止的字符串满足条件

		// 分割
		for (int i = 0; i < s.length(); i++) {
			int map[] = new int[26];
			boolean flag2 = false;// 从i开始,是否有长度大于2的子串满足条件
			for (int j = i; j < s.length(); j++) {
				map[s.charAt(j) - 'a']++;

				boolean flag = true;// i~j段是否满足条件
				for (int m : map) {
					if (m > 0 && m < k) {
						flag = false;
					}
				}

				if (flag) {// 若i~j子串符合条件,则将子串字符加入set中,并记录该子串的长度
					sets[j] = new HashSet<Character>();
					for (int kk = 0; kk < map.length; kk++) {
						if (map[kk] != 0) {
							sets[j].add((char) (kk + 'a'));
							len[j] += map[kk];
						}
					}
					i = j;
					flag2 = true;// 有子串满足条件
					break;
				}
			}
			if (flag2 == false) {// 当前没有满足条件的子串,第i字符定为分隔符(i~s.length为止没有子串符合条件)
				sets[i] = new HashSet<Character>();
			}

		}

		int pre = 0;// 找到第一个子串或分割字符
		for (int i = 0; i < s.length(); i++) {
			if (sets[i] != null) {
				pre = i;
				break;
			}
		}

		// 合并
		for (int i = pre + 1; i < s.length(); i++) {
			if (sets[i] != null) {
				if (sets[i].size() == 0 && sets[pre].size() != 0 && sets[pre].contains(s.charAt(i))) {
					sets[i].addAll(sets[pre]);
					len[i] = len[pre] + 1;
				} else if (sets[i].size() != 0 && sets[pre].size() != 0) {
					sets[i].addAll(sets[pre]);
					len[i] += len[pre];
				}
				pre = i;
			}
		}

		for (int i = 0; i < s.length(); i++) {
			max = Math.max(max, len[i]);
			// System.out.println(sets[i] == null ? "null" : sets[i].size() +
			// ":" + len[i]);
		}

		return max;
	}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值