置頂

GeoGebra 文章目錄

GeoGebra 文章目錄  更新日期:2018/2/8 我將 GeoGebra 相關的文章及檔案連結都整理在這篇裡,之後如果有新的文章也會同時更新這個目錄。上傳到 GeoGebraTube 的檔案,我有試著用 Google Chrome 63.0.3239.13...

熱門文章

2026年7月24日 星期五

LeetCode 解題筆記:3514. Number of Unique XOR Triplets II

作者:王一哲
日期:2026年7月24日


LeetCode 題目連結:3514. Number of Unique XOR Triplets II

解題想法


中等難度題。題目給一個陣列 $nums$,要找出從 $nums$ 之中任意取 3 個數字計算 XOR 的結果共有幾個,取的數字可以重複。由於答案只要不重複的計算結果數量,很適合用集合儲存計算結果。這題基本上就是硬算,大約分成以下的步驟:
  1. 將 $nums$ 轉成集合,移除重複的數字,存入 uni_nums。
  2. 由 uni_nums 任意取兩個數字計算 XOR,存入 uni_pairs。
  3. 由 uni_pairs 取一個數字,由 uni_nums 取一個數字,計算 XOR,存入 uni_triplets。
  4. 回傳 uni_triplets 的長度。


Python 程式碼


Runtime: 5873 ms, beats 64.81%. Memory: 19.72 MB, beats 22.22%.
class Solution:
    def uniqueXorTriplets(self, nums: List[int]) -> int:
        uni_nums = set(nums)  # 不重複的數字
        # 由提示2、3,任意選兩個數字計算 XOR
        uni_pairs = {x^y for x in uni_nums for y in uni_nums}
        # 取出 uni_pairs 之中一個數字與 uni_nums 之中一個數字計算 XOR
        uni_triplets = {p^z for p in uni_pairs for z in uni_nums}
        # 回傳答案
        return len(uni_triplets)

Runtime: 6679 ms, beats 61.11%. Memory: 19.70 MB, beats 46.30%.
class Solution:
    def uniqueXorTriplets(self, nums: List[int]) -> int:
        uni_nums = set(nums)  # 不重複的數字
        # 由提示2、3,任意選兩個數字計算 XOR
        uni_pairs = set()
        for y in uni_nums:
            for x in uni_nums:
                uni_pairs.add(x^y)
        # 取出 uni_pairs 之中一個數字與 uni_nums 之中一個數字計算 XOR
        uni_triplets = set()
        for p in uni_pairs:
            for z in uni_nums:
                uni_triplets.add(p^z)
        # 回傳答案
        return len(uni_triplets)


2026年7月23日 星期四

LeetCode 解題筆記:3513. Number of Unique XOR Triplets I

作者:王一哲
日期:2026年7月23日


LeetCode 題目連結:3513. Number of Unique XOR Triplets I

解題想法


中等難度題。這題考數學,題目給一個長度為 $n$ 的陣列 $nums$,且 $nums$ 的內容為任意排列的 $1$ 到 $n$,可以從 $nums$ 之中任取 3 個數字求 XOR,數字可以重複,求總共有幾個計算結果。這題如果用 3 層迴圈硬算一定會超時,要找出數學關係後直接輸出答案。如果 $n = 1$,只有 1 個計算結果 1^1^1 = 1。如果 $n = 2$,只有 2 個計算結果,例如 01^01^01 = 01, 01^01^10 = 10。如果 $n = 3$,最大值為 001^001^111 = 111,不可能超過 $111_2 = 8$,計算結果在 $0$ 到 $2^n - 1$ 之間,答案等於 $2^n$。

Python 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 31.84 MB, beats 91.53%.
class Solution:
    def uniqueXorTriplets(self, nums: List[int]) -> int:
        n = len(nums)
        # 特例,n = 1,1位數,只有 1^1^1 = 1
        if n == 1: return 1
        # 特例,n = 2,2位數,只有 01^01^01 = 1, 01^01^10 = 2
        if n == 2: return 2
        # 一般狀況,n >= 3,最大值為 n 個 1,即 2**n - 1,共有 2**n 個答案
        bits = n.bit_length()
        return 1 << bits


2026年7月22日 星期三

LeetCode 解題筆記:5. Longest Palindromic Substring

作者:王一哲
日期:2026年7月17日


LeetCode 題目連結:5. Longest Palindromic Substring

解題想法


中等難度題。題目給一個字串 $s$,要找出 $s$ 之中最長迴文子字串長度。這題我是用暴力解,先在 $s$ 兩端加上不可能出現的字元當作邊界,再分成奇數長度、偶數長度的子字串各處理一次。用一個 for 迴圈列舉所有可能的中心點索引值,如果是奇數長度中心點為 $c = 1$ 到 $c = len(s) - 2$,如果是偶數長度中心點為 $c = 1$ 到 $c = len(s) - 3$;再用一個 while 迴圈由 $c$ 向兩側延伸,如果兩側的字元相同,可以組長更長的迴文子字串,繼續向外延伸;當 while 迴圈結束時,更新答案。速度比想像中快很多。

Python 程式碼


Runtime: 155 ms, beats 96.12%. Memory: 19.18 MB, beats 91.25%.
class Solution:
    def longestPalindrome(self, s: str) -> str:
        s = '[' + s + ']'  # 兩端加上不可能出現的字元當作邊界
        # 處理奇數長度的字串,c 為中心點,r 為位移量值,檢查範圍為 s[c-r] ~ s[c+r]
        longest = ""  # 最長字串,預設為空字串
        for c in range(1, len(s)-1):  # 依序檢查 c = 1 ~ len(s)-2,要扣掉另外加上去的 []
            r = 1
            while s[c-r] == s[c+r]: r += 1  # 結束時 r 多加 1,回文字串長度為 2*(r-1)+1 = 2*r -1
            if r+r-1 > len(longest):  # 如果新找到的回文字串較長
                longest = s[c-r+1:c+r]
        # 處理偶數長度的字串,i 為中心點,r 為位移量值,檢查範圍為 s[i-r] ~ s[i+r+1]
        for i in range(1, len(s)-2):  # 依序檢查 i = 1 ~ len(s)-3,要扣掉另外加上去的 []
            r = 0
            while s[i-r] == s[i+r+1]: r += 1  # 結束時回文字串長度為 2*r
            if r+r > len(longest):
                longest = s[i-r+1:i+r+1]
        return longest


2026年7月21日 星期二

LeetCode 解題筆記:3499. Maximize Active Section with Trade I

作者:王一哲
日期:2026年7月21日


LeetCode 題目連結:3499. Maximize Active Section with Trade I

解題想法


中等難度題,題目給一個只包含 0、1 的字串 $s$,可以對 $s$ 操作 1 次,過程為
  1. 取一段連續的 1,其兩側皆為連續的 0,將中間的 1 全部改成 0。
  2. 再將上個步驟取出的 3 段都改成 1。
題目要計算操作後 $s$ 之中最多可以有幾個 1。因為以上的操作並不會讓原來的 1 消失,反而是兩側的 0 變成 1,如果要使操作後的 1 數量最多,就是要找出兩段 0 數量相加的最大值。解題時先依照題義補上兩側的 1,儲存成新的字串 $t$。接下來計算連續出現的 0, 1 長度,儲存至串列 $rle$。最後找出最大增益,取連續兩段 0 的總長度最大值 $imax$,回傳 $s$ 之中 1 的數量加上 $imax$。

Python 程式碼


Runtime: 561 ms, beats 85.98%. Memory: 21.08 MB, beats 61.68%.
class Solution:
    def maxActiveSectionsAfterTrade(self, s: str) -> int:
        t = "1" + s + "1"  # 依照題義補上兩側的 1
        n = len(t)  # 長度,s 的內容為 1 ~ n-2
        
        # --- 計算連續出現的 0, 1 長度 ---
        rle = []  # 遊程編碼,run-length encoding
        curr = '1'  # 目前的字元,最左側是 1
        cnt = 1  # 數量
        for i in range(1, n):  # 掃過字串 t
            if t[i] == curr:  # 相同的字元
                cnt += 1  # 數量加 1
            else:  # 不同的字元,結算前一段
                rle.append(cnt)
                curr = t[i]
                cnt = 1
        rle.append(cnt)  # 結算最後一段

        # --- 找出最大增益,取連續兩段 0 的總長度最大值 ---
        imax, m = 0, len(rle)  # 最大值,rle 長度
        for i in range(2, m-2, 2):  # i 只找 1 所在的位置,排除兩端
            imax = max(imax, rle[i-1] + rle[i+1])
        # 答案為 s 之中 1 的數量加上 imax
        return s.count('1') + imax


2026年7月20日 星期一

LeetCode 解題筆記:1260. Shift 2D Grid

作者:王一哲
日期:2026年7月20日


LeetCode 題目連結:1260. Shift 2D Grid

解題想法


簡單題。題目給一個大小為 $m \times n$ 的二維陣列 $grid$,依照以下的 3 個規則操作 $k$ 次,回傳操作後的陣列。
  1. 將 $grid[i][j]$ 移到 $grid[i][j+1]$
  2. 將 $grid[i][n-1]$ 移到 $grid[i+1][0]$
  3. 將 $grid[m-1][n-1]$ 移到 $grid[0][0]$
由於這題的操作次數 $k$ 最多為 $100$ 次,可以直接模擬移動過程,不過這樣寫速度會比較慢。

我們可以觀察範例
grid = [[1,2,3],[4,5,6],[7,8,9]]
操作 1 次之後變成
[[9,1,2],[3,4,5],[6,7,8]]
如果將原來的二維陣列頭尾相接成一維陣列,以上的操作就是所有元素向後平移一格,最後一格移到最前面。利用這個性質,我們可以先將操作次數 $k$ 對 $m \times n$ 取餘數,因為每操作 $m \times n$ 次陣列會恢愎原狀。用兩層 for 迴圈掃過陣列,外層 $i = 0$ 到 $i = m-1$,內層 $j = 0$ 到 $j = n-1$,對應到平移後的一維陣列索引值 $pos = i \times n + j + k \pmod {m \times n}$,再換回二維陣列的索引值 $[pos / n, pos \pmod n]$。

Python 程式碼


直接模擬操作過程。Runtime: 151 ms, beats 12.14%. Memory: 19.66 MB, beats 48.15%.
class Solution:
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
        m, n = len(grid), len(grid[0])
        mat = [[0] * n for _ in range(m)]
        for _ in range(k):
            for i in range(m):
                for j in range(n):
                    mat[i][(j + 1) % n] = grid[i][j]
            last = mat[m-1][0]
            for i in range(m-1, 0, -1):
                mat[i][0] = mat[i-1][0]
            mat[0][0] = last
            grid, mat = mat, grid
        return grid

當作一維串列計算平移後的索引值。Runtime: 3 ms, beats 83.13%. Memory: 19.40 MB, beats 48.15%.
class Solution:
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
        # 當成1維串列計算索引值,向右平移 k 格,再轉回2維串列
        m, n = len(grid), len(grid[0])
        tot = m * n
        k %= tot
        ans = [[0] * n for _ in range(m)]  # 儲存答案用的2維串列
        for i in range(m):
            for j in range(n):
                pos = (i * n + j + k) % tot
                ans[pos // n][pos % n] = grid[i][j]
        return ans

攤平成一維串列,用切片平移串列,再填回二維串列之中。Runtime: 3 ms, beats 83.13%. Memory: 19.75 MB, beats 19.55%.
class Solution:
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
        # 拉平成1維串列,向右平移 k 格,再轉回2維串列
        m, n = len(grid), len(grid[0])
        k %= m * n
        arr = [val for row in grid for val in row]
        arr = arr[m*n - k:] + arr[:m*n - k]
        for i in range(m):
            grid[i] = arr[i*n : (i+1)*n]
        print(arr)
        return grid


2026年7月19日 星期日

LeetCode 解題筆記:1081. Smallest Subsequence of Distinct Characters

作者:王一哲
日期:2026年7月19日


LeetCode 題目連結:1081. Smallest Subsequence of Distinct Characters

解題想法


中等難度題。題目給一個字串 $s$,回傳字串中最小字典序的子字串,而且子字串之中每個字母只能出現一次。這題可以用單調隊列解題,隊列中的字母依照字典序排列,遇到新的字母 c 時,從隊列最後面移除字典序大於 c 的字母。在 Python 可以用 list 或是 string 儲存隊列,在 C++ 可以用 vector 或是 string 儲存隊列。另外要記錄每個字母最後一次於 s 出現的索引值,以及隊列中目前已選的字母。

Python 程式碼


用字典儲存索引值及已選的字母,用串列儲存隊列。Runtime: 3 ms, beats 38.55%. Memory: 19.24 MB, beats 75.23%.
class Solution:
    def smallestSubsequence(self, s: str) -> str:
        # 記錄 s 之中每個字母最後一次出現的索引值
        lastIdx = {chr(i + ord('a')): -1 for i in range(26)}
        for i, c in enumerate(s):
            lastIdx[c] = i
        # 單調隊列,t 之中的字母按照字典序排列,used 記錄 t 之中是否有字母 c
        t = []
        used = {chr(i + ord('a')): False for i in range(26)}
        for i, c in enumerate(s):
            if used[c]: continue  # 已有字母 c,跳過
            # 如果 t 有資料,c 小於 t 最後一項,而且 i 小於 t[-1] 最後一次出現的索引值
            # 後面還有與 t[-1] 相同的字母可以加入,先移除
            while t and c < t[-1] and i < lastIdx[t[-1]]:
                used[t.pop()] = False  # 移除 t[-1] 並重設 used
            # c 加入 t 最後面
            t.append(c)
            used[c] = True
        # 接成字串並回傳
        return "".join(t)

用串列儲存索引值、已選的字母及隊列。Runtime: 3 ms, beats 38.55%. Memory: 19.55 MB, beats 75.23%.
class Solution:
    def smallestSubsequence(self, s: str) -> str:
        # 記錄 s 之中每個字母最後一次出現的索引值
        lastIdx = [-1] * 26
        for i, c in enumerate(s):
            lastIdx[ord(c) - ord('a')] = i
        # 單調隊列,t 之中的字母按照字典序排列,used 記錄 t 之中是否有字母 c
        t = []
        used = [False] * 26
        for i, c in enumerate(s):
            if used[ord(c) - ord('a')]: continue  # 已有字母 c,跳過
            # 如果 t 有資料,c 小於 t 最後一項,而且 i 小於 t[-1] 最後一次出現的索引值
            # 後面還有與 t[-1] 相同的字母可以加入,先移除
            while t and c < t[-1] and i < lastIdx[ord(t[-1]) - ord('a')]:
                used[ord(t.pop()) - ord('a')] = False  # 移除 t[-1] 並重設 used
            # c 加入 t 最後面
            t.append(c)
            used[ord(c) - ord('a')] = True
        # 接成字串並回傳
        return "".join(t)

用串列儲存索引值、已選的字母,用字串儲存隊列。Runtime: 3 ms, beats 38.55%. Memory: 19.36 MB, beats 39.11%.
class Solution:
    def smallestSubsequence(self, s: str) -> str:
        # 記錄 s 之中每個字母最後一次出現的索引值
        lastIdx = [-1] * 26
        for i, c in enumerate(s):
            lastIdx[ord(c) - ord('a')] = i
        # 單調隊列,t 之中的字母按照字典序排列,used 記錄 t 之中是否有字母 c
        t = ""
        used = [False] * 26
        for i, c in enumerate(s):
            if used[ord(c) - ord('a')]: continue  # 已有字母 c,跳過
            # 如果 t 有資料,c 小於 t 最後一項,而且 i 小於 t[-1] 最後一次出現的索引值
            # 後面還有與 t[-1] 相同的字母可以加入,先移除
            while t and c < t[-1] and i < lastIdx[ord(t[-1]) - ord('a')]:
                used[ord(t[-1]) - ord('a')] = False  # 移除 t[-1] 並重設 used
                t = t[:-1]
            # c 加入 t 最後面
            t += c
            used[ord(c) - ord('a')] = True
        # 回傳 t
        return t


2026年7月18日 星期六

LeetCode 解題筆記:1979. Find Greatest Common Divisor of Array

作者:王一哲
日期:2026年7月18日


LeetCode 題目連結:1979. Find Greatest Common Divisor of Array

解題想法


簡單題。題目給一個陣列 $nums$,回傳陣列中最小值與最大值的最大公因數。因為 Python 與 C++ 都有找最小值、最大值、最大公因數的工具,可以一行解。如果不使用這些工具,也可以用一個 for 迴圈掃過 $nums$ 找最小值與最大值,另外再寫一個自訂函式用輾轉相除法求最大公因數。因為測資很小,兩種寫法都很快。

Python 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 19.26 MB, beats 80.59%.
class Solution:
    def findGCD(self, nums: List[int]) -> int:
        return gcd(min(nums), max(nums))

Runtime: 0 ms, beats 100.00%. Memory: 19.27 MB, beats 80.59%.
class Solution:
    def findGCD(self, nums: List[int]) -> int:
        imin, imax = float('inf'), float('-inf')
        for num in nums:
            if num < imin:
                imin = num
            if num > imax:
                imax = num
        
        def mygcd(a, b):
            while b:
                a, b = b, a%b
            return a
        
        return mygcd(imin, imax)


2026年7月17日 星期五

LeetCode 解題筆記:4. Median of Two Sorted Arrays

作者:王一哲
日期:2026年7月17日


LeetCode 題目連結:4. Median of Two Sorted Arrays

解題想法


困難題。題目給兩個已經由小到大排序好的陣列 $nums1$ 及 $nums2$,要找出兩個陣列合併後的中位數,如果合併後陣列中整數數量 $n$ 為奇數,中位數是索引值為 $\left \lfloor n/2 \right \rfloor$ 的數字;如果數量為偶數,中位數則是取索引值為 $n/2 - 1$ 及 $n/2$ 兩個數的平均值。

由於我以前在 ZeroJudge 寫過類似的題目,比較直覺的想法是用一個最小優先佇列 $large$ 儲存目前大於中位數的數字,用一個最大優先佇列 $small$ 儲存目前小於中位數的數字,用一個 for 迴圈依序取出陣列中的數字 $x$,拿 $x$ 與 $large$ 及 $small$ 最上面的值比大小,決定 $x$ 要放入 $large$ 或是 $small$;再調整 $large$ 與 $small$ 的長度,讓兩者長度相同或是 $small$ 比 $large$ 多一項。這個寫法能夠過關,但是速度不夠快。

另一個寫法是依序取出 $nums1$ 及 $nums2$ 的數字 $x$,利用內建的二分搜尋法工具找到 $x$ 於合併後的陣列 $nums$ 之中插入數字並保持由小到大排序的索引值。這個寫法程式碼很簡單,速度也快很多。

Python 程式碼


Runtime: 18 ms, beats 5.26%. Memory: 19.68 MB, beats 14.45%.
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        large, small = [], []
        nums = nums1[:] + nums2[:]
        for x in nums:
            if not small or x < -small[0]:
                heapq.heappush(small, -x)
            else:
                heapq.heappush(large, x)
            if len(small) > len(large) + 1:
                v = -heapq.heappop(small)
                heapq.heappush(large, v)
            if len(large) > len(small):
                v = heapq.heappop(large)
                heapq.heappush(small, -v)
        mid = -small[0]
        if len(small) == len(large):
            mid = (-small[0] + large[0]) * 0.5
        return mid

Runtime: 7 ms, beats 13.14%. Memory: 19.72 MB, beats 14.45%.
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        nums = []
        for x in nums1:
            bisect.insort(nums, x)
        for x in nums2:
            bisect.insort(nums, x)
        
        n = len(nums)
        if n % 2 == 1:
            return nums[n//2]
        else:
            return (nums[n//2 - 1] + nums[n//2]) * 0.5
        return mid


2026年7月16日 星期四

LeetCode 解題筆記:3867. Sum of GCD of Formed Pairs

作者:王一哲
日期:2026年7月16日


LeetCode 題目連結:3867. Sum of GCD of Formed Pairs

解題想法


中等難度的題目。題目給一個長度為 $n$ 的陣列 $nums$,依序取 $nums$ 前綴的最大值,並將最大值與 $nums[i]$ 取最大公因數,填入陣列 $prefixGcd$。填完 $prefixGcd$ 之後,將 $prefixGcd$ 由小到大排序。最後由 $prefixGcd$ 兩端往中央取值,計算 $prefixGcd[i], prefixGcd[n-i-1]$ 的最大公因數,將最大公因數加總求答案。只要按照題目的規則寫程式就好,不需要想太多。

Python 程式碼


Runtime: 187 ms, beats 75.45%. Memory: 34.02 MB, beats 37.13%.
class Solution:
    def gcdSum(self, nums: list[int]) -> int:
        # 計算 prefixGcd 再排序
        n = len(nums)
        imax = nums[0]
        prefixGcd = [imax] + [0] * (n-1)
        for i in range(1, n):
            imax = max(imax, nums[i])
            prefixGcd[i] = gcd(nums[i], imax)
        prefixGcd.sort()
        
        # 由 prefixGcd 兩端向中央取值,計算兩者的 gcd 再相加
        ans = 0
        for i in range(n//2):
            ans += gcd(prefixGcd[i], prefixGcd[n-i-1])
        return ans


2026年7月15日 星期三

LeetCode 解題筆記:3658. GCD of Odd and Even Sums

作者:王一哲
日期:2026年7月15日


LeetCode 題目連結:3658. GCD of Odd and Even Sums

解題想法


簡單題。這題考數學,直接回傳 $n$ 即可。前 $n$ 個正的奇數和為 $$ sumOdd = 1 + 3 + 5 + \dots + (2n - 1) = \frac{(2n - 1 + 1) \times n}{2} = n^2 $$ 前 $n$ 個正的偶數和為 $$ sumEven = 2 + 4 + 6 + \dots + 2n = \frac{(2n + 2) \times n}{2} = n(n+1) $$ 因為 $n$ 與 $n+1$ 的最大公因數為 $1$,因此 $sumOdd$ 與 $sumEven$ 的最大公因數為 $n$。

如果沒有想到以上的數學性質,真的用迴圈算出 $sumOdd$ 與 $sumEven$ 的值再取最大公因數也可能,因為題目的 $n$ 最大為 $1000$,很快就能算完。

Python 程式碼


Runtime: 0 ms, beats 100.00%. Memory: 19.18 MB, beats 85.77%.
class Solution:
    def gcdOfOddEvenSums(self, n: int) -> int:
        return n

Runtime: 11 ms, beats 38.21%. Memory: 19.24 MB, beats 53.03%.
class Solution:
    def gcdOfOddEvenSums(self, n: int) -> int:
        sumOdd = sum(range(1, 2*n, 2))
        sumEven = sum(range(2, 2*n + 1, 2))
        return gcd(sumOdd, sumEven)