【LeetCodeに挑戦】387.First Unique Character in a String

現在ITエンジニアとして働いていますが、コーディング・アルゴリズム力をもっと鍛えたいと思い、LeetCodeで学んだ内容をメモ的にアウトプットしています。
今回解いてみた問題は「387.First Unique Character in a String」です。

問題

文字列sが与えられており、最初のユニーク文字のインデックス番号を求める。存在しない場合は-1を返す。(難易度はEasy)

Input: s = "leetcode"

Output: 0

Explanation:

The character 'l' at index 0 is the first character that does not occur at any other index.
Input: s = "loveleetcode"

Output: 2
Example 3:

Input: s = "aabb"

Output: -1

制約

  • 1 <= s.length <= 10^5

  • s consists of only lowercase English letters.

私のダメな解法

class Solution:
    def firstUniqChar(self, s: str) -> int:
        for i in range(len(s)):
            this_word = s[i]
            rest_words = s[:i] + s[i+1:]
            if this_word not in rest_words:
                return i
        
        return -1

このコードの課題

■時間計算量が高い(O(n²))
スライス操作 (s[:i] + s[i+1:]): 各ループで文字列をスライスし、結合する操作は O(n) の時間がかかる。
not in 操作 (this_word not in rest_words): O(n) の時間がかかる。
結果的に各ループで O(n) の操作が行われ、それが n 回 繰り返されるため、全体の時間計算量は O(n²) となる。

■スペースの非効率な使用
文字列の長さ n に対して、各ループで O(n) のメモリが必要となり、全体で O(n²) のメモリを消費する。

■文字の存在確認が非効率(not in 操作)
文字列内での not in 操作は、内部的には線形探索を行い、一つずつ文字を比較する。最悪の場合、文字列全体をチェックする必要がある。
各ループでの存在確認が O(n) の時間を要する。

理想的な解法

class Solution:
    def firstUniqChar(self, s: str) -> int:
        """
        :type s: str
        :rtype: int
        """
        # build hash map: character and how often it appears
        count = collections.Counter(s)
        
        # find the index
        for idx, ch in enumerate(s):
            if count[ch] == 1:
                return idx     
        return -1

学んだこと:アルゴリズム

  • collections.Counterを使用して、各文字が文字列 s に何回出現するかをカウントできる。

  • スライス操作 (s[:i] + s[i+1:]) や結合操作(+)は、文字列の一部を新しくコピーして結合するため、文字列の長さに比例して時間がかかる。

  • 文字セットが固定(例えば、English Alphabet 26 letters)である場合、空間計算量は入力の大きさ n に依存せず、O(1)と判断できる。

いいなと思ったら応援しよう!