Day 6:2981. 找出出现至少三次的最长特殊子字符串 I

127 阅读4分钟

Leetcode 2981. 找出出现至少三次的最长特殊子字符串 I

给你一个仅由小写英文字母组成的字符串 s 。

如果一个字符串仅由单一字符组成,那么它被称为 特殊 字符串。例如,字符串 "abc" 不是特殊字符串,而字符串 "ddd"、"zz" 和 "f" 是特殊字符串。

返回在 s 中出现 至少三次最长特殊子字符串的长度,如果不存在出现至少三次的特殊子字符串,则返回 -1 。

子字符串 是字符串中的一个连续 非空 字符序列。

image.png

首先需要明确两个概念:

  • 特殊字符串:只有单一字符组成的字符串。
  • 出现:这里是可以是可以重复,比如 "aaa",出现两次是:前两个字符一次,后两个字符一次。

对于一个长度为 n 的特殊字符串:

  • 出现一次的最长长度为 n;
  • 出现两次的最长长度为 n - 1;
  • 出现三次的最长长度为 n - 2;
  • 出现 m 次的最长长度为 n -m - 1 (m < n)。

出现三次的特殊字符串,最理想的情况,是这个特殊字符串出现字符串中的三处地方(中间是有其他字符隔开的),比如 "aabaabaa",那么就可以提取每段,就是出现三次,这种情况下最长长度就是每段出现一次,最长长度就是最短的那段的长度。
但是并不是分为三段,也有可能是两段,这时,要想获得最长长度,让更长的那段中出现两次,更短的那段出现一次。
当然还有一种情况就是只有一段,那么就必须让这段出现三次才能获取到最长长度。

字符串中会出现多段:

  • 如果考虑只在一段中找到最长长度,最长长度是 n - 2,那选择在最长的那段中找到;
  • 如果考虑在两段中找到最长长度,最长长度是让更长的那段出现两次 n - 1,另一段出现一次 n,那选择最长的两段中找到结果;
  • 如果考虑在三段中找到最长长度,最长长度就是每段出现一次,最长长度就是最短那段的长度;
  • 如果考虑在大于三段中找到最长长度,选择其中的三段进行考虑,那么就是选择最长的三段考虑得到最长长度。因此,只需要保存最长三段的长度即可。

有三段,可能考虑三段不是最优结果,也有可能考虑两段的时候结果更好哟!!也有可能是一段哟!!!

用一个数组保存每个字母每段的长度,题中说明字符串都是小写字母,并且只需要保存最长的三段(保持递增更好计算哦),因此使用一个 26 * 4 的数组(保存最长的三段使用 *4 的原因后续说明)。

首先统计每段特殊字符串的长度,一个字符也需要考虑哦:

int i = 0
while (i < len) {
    int cnt = 1;  // 特殊字符串长度
    char c = s.charAt(i);
    while ((i < len - 1) && (s.charAt(i + 1) == c)) {
        i++;
        cnt++;
    }
    i++;
}

保存数据,每次将数据插入到最后一个位置,从后往前冒泡使其放入正确的位置保持递增,因此才使用 *4。

// 冒泡排序插入
list[c - 'a'][3] = cnt;
for (int j = 2; j >= 0; j--) {
    if (list[c - 'a'][j] < list[c - 'a'][j + 1]) {
        int tmp = list[c - 'a'][j + 1];
        list[c - 'a'][j + 1] = list[c - 'a'][j];
        list[c- 'a'][j] = tmp;
    } else {
        break;
    }
}

最后传入每个字母组成的的特殊字符串数组,进行判断得到最长长度

/**
  * 一个长度为 n 的字符串
  * 出现一次最长长度为 n
  * 出现两次最长长度为 n - 1
  * 出现三次最长长度为 n - 2
 */
public int maxLength(int[] list) {
    if (list[0] == 0) return -1;	// 字母没有出现
    int res = list[0] - 2;	// 考虑一段
    if (list[1] != 0) {		// 考虑两段
        int tmp = list[0] - 1;
        tmp = Math.min(tmp, list[1]);
        res = Math.max(tmp, res);
    }
    if (list[2] != 0) {		// 考虑三段
        res = Math.max(res, list[2]);
    }

    if (res <= 0) return -1;	// 无效返回 -1
    return res;
}

完整代码:

class Solution {
    public int maximumLength(String s) {
        int[][] list = new int[26][4];  
        
        int len = s.length();
        int i = 0;
        while (i < len) {
            int cnt = 1; // 特殊字符串长度
            char c = s.charAt(i);
            while ((i < len - 1) && (s.charAt(i + 1) == c)) {
                i++;
                cnt++;
            }
            i++;

            // 冒泡排序插入
            list[c - 'a'][3] = cnt;
            for (int j = 2; j >= 0; j--) {
                if (list[c - 'a'][j] < list[c - 'a'][j + 1]) {
                    int tmp = list[c - 'a'][j + 1];
                    list[c - 'a'][j + 1] = list[c - 'a'][j];
                    list[c- 'a'][j] = tmp;
                } else {
                    break;
                }
            }
        }

        int res = -1;

        for (i = 0; i < 26; i++) {
            res = Math.max(res, maxLength(list[i]));
        }
        return res;

    }

    /**
      * 一个长度为 n 的字符串
      * 出现一次最长长度为 n
      * 出现两次最长长度为 n - 1
      * 出现三次最长长度为 n - 2
     */
    public int maxLength(int[] list) {
        if (list[0] == 0) return -1;
        int res = list[0] - 2;
        if (list[1] != 0) {
            int tmp = list[0] - 1;
            tmp = Math.min(tmp, list[1]);
            res = Math.max(tmp, res);
        }
        if (list[2] != 0) {
            res = Math.max(res, list[2]);
        }

        if (res <= 0) return -1;
        return res;
    }
}