LeetCode 830. 較大分組的位置
## 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
思路
需要識別字符串陣列中連續出現的字元,因此想到正向雙指標。
思路步驟:
- 分別使 i 和 j 指向字串 s 中的前兩個字元,判斷是否相等。
- 如果不相等,則 i 和 j 分別往右挪一位, i++ ,j++ ,重新判斷是否相等。
- 如果相等,固定指標 i 不動,將 j 往後挪動(j++),每挪一位判斷與 i 位字元是否相等,並用 count 計數相等的字元個數。
- 當連續字元結束時,即 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),我們只需要常數的空間來儲存若干變數,注意返回值不計入空間複雜度。