1. 程式人生 > 其它 >LeetCode 830. 較大分組的位置

LeetCode 830. 較大分組的位置

技術標籤:LeetCode指標字串

## 830. 較大分組的位置
難度:Easy
語言:Java

題目

在一個由小寫字母構成的字串 s 中,包含由一些連續的相同字元所構成的分組。
例如,在字串 s = “abbxxxxzyy” 中,就含有 “a”, “bb”, “xxxx”, “z” 和 “yy” 這樣的一些分組。
分組可以用區間 [start, end] 表示,其中 start 和 end 分別表示該分組的起始和終止位置的下標。上例中的 “xxxx” 分組用區間表示為 [3,6] 。
我們稱所有包含大於或等於三個連續字元的分組較大分組
找到每一個較大分組的區間,按起始位置下標遞增順序排序後

,返回結果。
注意:1 <= s.length <= 1000,且s 僅含小寫英文字母。

示例 1:
輸入:s = “abbxxxxzzy”
輸出:[[3,6]]
解釋:“xxxx” 是一個起始於 3 且終止於 6 的較大分組。

示例 2
輸入:s = “abc”
輸出:[]
解釋:“a”,“b” 和 “c” 均不是符合要求的較大分組。

示例 3
輸入:s = “abcdddeeeeaabbbcd”
輸出:[[3,5],[6,9],[12,14]]
解釋:較大分組為 “ddd”, “eeee” 和 “bbb”

提示:

解法1

思路

需要識別字符串陣列中連續出現的字元,因此想到正向雙指標。

題目要求找到每一個較大分組的區間,按起始位置下標遞增順序排序後,返回結果。其實沒必要再排序,只要按下標順序一次找到較大分組,就已經是遞增排序。
思路步驟:

  1. 分別使 i 和 j 指向字串 s 中的前兩個字元,判斷是否相等。
  2. 如果不相等,則 i 和 j 分別往右挪一位, i++ ,j++ ,重新判斷是否相等。
  3. 如果相等,固定指標 i 不動,將 j 往後挪動(j++),每挪一位判斷與 i 位字元是否相等,並用 count 計數相等的字元個數。
  4. 當連續字元結束時,即 i 與 j 指向的字元不再相等時,判斷 count 是否大於等於3。若count>=3,則區間 [i, j-1] 構成較大分組,將其新增至結果列表中。然後將 i 移動到 j 的位置,再將 j 向右挪一位,繼續遍歷。若count<3,則不是較大分組,直接移動 i 和 j 的位置,繼續遍歷,直到 j 移動到字串末尾為止。

i += (j-i); // i 先移動到 j 的位置
j++; // j 再往右移一位

程式碼

class Solution {
    public List<List<Integer>> largeGroupPositions(String s) {
        int len = s.length(), i=0, j=1, count;
        List<List<Integer>> list = new ArrayList<>();
        while (j < len){
            while (s.charAt(i) != s.charAt(j)){ //錯誤1:前面有重複項,並以兩個不同字母結尾會報錯 陣列越界
                i++;
                if (j == len-1) break;
                j++;//錯誤1修改(當j++跟在i++後時發生錯誤1)
            }
            /*
            不需要判斷退出上一步while的原因:1.條件不符 2.break
                1.如果是條件不符退出  可繼續計算有幾個重複項
                2.如果是break退出,count=1,後面已經無項,不會被記錄,j++後退出迴圈 */
            count = 1;
            while(s.charAt(i) == s.charAt(j)){
                count++;
                if (j == len-1) {
                    j++;//錯誤2修改
                    break;//錯誤2:"aaa"誤解得[0,1],只有一個重複項時,當j==len-1就退出,後面largeList.add(j-1)會少記錄一個
                }
                j++;
            }
            //如果該重複組count≥3,記錄為較大分組[i, j-1]
            if (count >= 3){
                List<Integer> largeList = new ArrayList<>();
                largeList.add(i);
                largeList.add(j-1);
                list.add(largeList);
            }
            //繼續遍歷
            i += (j-i); j++;// i先移動到j的位置,j再往右移一位
        }
        return list;
    }
}

解法2(官方題解)

思路

首先看上面的解法1程式碼,包含了三個while迴圈(一個外部while巢狀兩個內部while),根據三個while的條件可發現,兩個內部while的條件分別是

while (s.charAt(i) != s.charAt(j))
while(s.charAt(i) == s.charAt(j))

而這兩個條件本質上是同一個邏輯判斷的兩個不同結果,可以合併。
另一個外部while的條件是

 while (j < len)

也可以和上面兩個while合併,只使用一個迴圈,完成遍歷。

for (int i = 0; i < len; i++) {
            if (i == len - 1 || s.charAt(i) != s.charAt(i + 1)) {
            //如果指標i指向字串尾部,或者i和i+1位字元不等,判斷count值
            //如果count>=3,是較大分組,新增到結果列表,然後count重置為1
            //如果count<3,直接將count重置為1,繼續遍歷
                if (count >= 3) {
                    list.add(Arrays.asList(i - count + 1, i));
                }
                count = 1;
            } 
            else {//如果指標i沒到尾部,並且i和i+1位字元相等
                count++;
            }
        }

可以看出,官方題解不僅只用了一次迴圈,與解法一相比,還少用了一個指標 j ,用 (i - count + 1)來表示區間的開始,用 i 來表示區間的結尾。

程式碼

class Solution {
    public List<List<Integer>> largeGroupPositions(String s) {
        List<List<Integer>> list = new ArrayList<List<Integer>>();
        int len = s.length();
        int count = 1;
        for (int i = 0; i < len; i++) {
            if (i == len - 1 || s.charAt(i) != s.charAt(i + 1)) {
                if (count >= 3) {
                    list.add(Arrays.asList(i - count + 1, i));
                }
                count = 1;
            }
            else {
                count++;
            }
        }
        return list;
    }
}

複雜度分析

時間複雜度:O(N),其中 N 是字串的長度。我們只需要遍歷一次該陣列
空間複雜度:O(1),我們只需要常數的空間來儲存若干變數,注意返回值不計入空間複雜度。