我们设 n 为字符串 text 的长度,下标从 0 开始,现在有一段区间 [i, j) (不包括 j )由相同字符 a 构成,并且该区间两边不存在相同的字符 a,而整个 text 中 a 的出现次数为 count}[a],那么当 count}[a] \gt j - i ,并且 i > 0 或者 j \lt n 时,可以将其他地方出现的 a 与 text}[i-1] 或 text}[j] 交换,从而得到更长的一段仅包含字符 a 的子串。
交换后,交换过来的 a 可能会使得两段连续的 a 拼接在一起,我们假设 [i, j) 是前面的一段,当 text}[j+1] = a 时,我们在找到后面的一段 [j + 1, k),这两段拼接在一起构成更长的子串。注意,我们需要重新判断是否有多余的 a 交换到中间来,因此将拼接后的长度 k - i 与 count}[a] 取最小值来更新答案。
classSolution { public: intmaxRepOpt1(string text){ unordered_map<char, int> count; for (auto c : text) { count[c]++; }
int res = 0; for (int i = 0; i < text.size(); ) { // step1: 找出当前连续的一段 [i, j) int j = i; while (j < text.size() && text[j] == text[i]) { j++; } int cur_cnt = j - i;
// step2: 如果这一段长度小于该字符出现的总数,并且前面或后面有空位,则使用 cur_cnt + 1 更新答案 if (cur_cnt < count[text[i]] && (j < text.size() || i > 0)) { res = max(res, cur_cnt + 1); }
// step3: 找到这一段后面与之相隔一个不同字符的另一段 [j + 1, k),如果不存在则 k = j + 1 int k = j + 1; while (k < text.size() && text[k] == text[i]) { k++; } res = max(res, min(k - i, count[text[i]])); i = j; } return res; } };
publicclassSolution { publicintMaxRepOpt1(string text) { IDictionary<char, int> count = new Dictionary<char, int>(); foreach (char c in text) { count.TryAdd(c, 0); count[c]++; }
int res = 0; for (int i = 0; i < text.Length; ) { // step1: 找出当前连续的一段 [i, j) int j = i; while (j < text.Length && text[j] == text[i]) { j++; } int curCnt = j - i;
staticinlineintmin(int a, int b) { return a < b ? a : b; }
staticinlineintmax(int a, int b) { return a > b ? a : b; }
intmaxRepOpt1(char * text) { int len = strlen(text); int count[26]; memset(count, 0, sizeof(count)); for (int i = 0; i < len; i++) { count[text[i] - 'a']++; }
int res = 0; for (int i = 0; i < len; ) { // step1: 找出当前连续的一段 [i, j) int j = i; while (j < len && text[j] == text[i]) { j++; } int cur_cnt = j - i;
// step2: 如果这一段长度小于该字符出现的总数,并且前面或后面有空位,则使用 cur_cnt + 1 更新答案 if (cur_cnt < count[text[i] - 'a'] && (j < len || i > 0)) { res = max(res, cur_cnt + 1); }
// step3: 找到这一段后面与之相隔一个不同字符的另一段 [j + 1, k),如果不存在则 k = j + 1 int k = j + 1; while (k < len && text[k] == text[i]) { k++; } res = max(res, min(k - i, count[text[i] - 'a'])); i = j; } return res; }
classSolution: defmaxRepOpt1(self, text: str) -> int: count = Counter(text) res = 0 i = 0 while i < len(text): # step1: 找出当前连续的一段 [i, j) j = i while j < len(text) and text[j] == text[i]: j += 1
# step2: 如果这一段长度小于该字符出现的总数,并且前面或后面有空位,则使用 cur_cnt + 1 更新答案 cur_cnt = j - i if cur_cnt < count[text[i]] and (j < len(text) or i > 0): res = max(res, cur_cnt + 1)
# step3: 找到这一段后面与之相隔一个不同字符的另一段 [j + 1, k),如果不存在则 k = j + 1 k = j + 1 while k < len(text) and text[k] == text[i]: k += 1 res = max(res, min(k - i, count[text[i]])) i = j return res
var maxRepOpt1 = function(text) { const count = newMap(); for (let i = 0; i < text.length; i++) { const c = text[i]; count.set(c, (count.get(c) || 0) + 1); }
let res = 0; for (let i = 0; i < text.length; ) { // step1: 找出当前连续的一段 [i, j) let j = i; while (j < text.length && text[j] === text[i]) { j++; } let curCnt = j - i;
// step2: 如果这一段长度小于该字符出现的总数,并且前面或后面有空位,则使用 curCnt + 1 更新答案 if (curCnt < (count.get(text[i] || 0)) && (j < text.length || i > 0)) { res = Math.max(res, curCnt + 1); }
// step3: 找到这一段后面与之相隔一个不同字符的另一段 [j + 1, k),如果不存在则 k = j + 1 let k = j + 1; while (k < text.length && text[k] === text[i]) { k++; } res = Math.max(res, Math.min(k - i, (count.get(text[i]) || 0))); i = j; } return res; };
复杂度分析
时间复杂度:O(n),其中 n 是字符串 text 的长度。求解每种字符出现次数的时间复杂度为 O(n),而每一段包含相同字符的区间最多会被遍历两次,因此总体时间复杂度为 O(n)。