LeetCode-387. First Unique Character in a String

LeetCode-387. First Unique Character in a String

難度: easy

Given a string sfind the first non-repeating character in it and return its index. If it does not exist, return -1.

給一個字串s

找到第一個沒有重複的字並回傳他的index,如果沒有則回傳 -1。

(鳥翻譯請見諒,如有錯誤請協助指出,感謝

Example 1:

Input: s = "leetcode"
Output: 0

Example 2:

Input: s = "loveleetcode"
Output: 2

Example 3:

Input: s = "aabb"
Output: -1

Constraints:

  • 1 <= s.length <= 105
  • s consists of only lowercase English letters.

解題流程

  1. 先看懂題目想描述甚麼情境
  2. 開始想像程式運作該經過哪些流程
  3. 將經過的流程所需的判斷跟細節列出
  4. 實作列出的流程

先看懂題目想描述甚麼情境

應該是找出第一個不重複的字,首先想到是邊遍歷字串,邊記錄目前出現幾次,最後再重新找第一個不重複的字。

開始想像程式運作該經過哪些流程

想像中應該需要一個Map來記錄目前找到的字串總共出現幾次,遍歷字串 s 時,遇到沒有在Map裡面的字,放入Map裡面,遇到已經在Map裡面的字則直接更新出現次數,只有遍歷到底的時候,才可以確定字串 s 中不重複的第一個字是哪一個。

想像如果在邊遍歷字串 s 時,設定一個變數為字串s 的index 0 開始確認不重複的第一個字,如果目前找的字已經出現在Map,則更新變數為下一個index,等到遍歷完字串 s 後,也可以找出字串 s 中不重複的第一個字。

將經過的流程所需的判斷跟細節列出

設定一個Map紀錄出現單字,和該單字出現次數。
遍歷字串s
如果有出現在Map
Map當中單字出現次數+1
如果沒有出現在Map
Map當中加上單字,單字出現次數+1
跑完遍歷字串s後,Map上面已經紀錄所有單字跟出現次數了。
設定一個變數紀錄第一個出現的不重複單字位置,重新遍歷一次字串s,如果今天出現在Map中的次數為1則return目前遍歷到的index,沒有的話則繼續遍歷。
如果走到最後都沒有return 則回傳 -1

實作列出的流程

typescript

function firstUniqChar(s: string): number {
    // trying to loop once to record appear times and record the firstIndex
    /**
        set 1 Map
     */
    const charMap = {};
    let firstAppear = 0;
    for(let i=0; i < s.length; i++) {
        const char = s[i];
        if(!charMap[char]) {
            charMap[char] = 0;
        }
        charMap[char]++
    }

    while(charMap[s[firstAppear]]) {
        if(charMap[s[firstAppear]] === 1) {
            return firstAppear;
        }
        firstAppear++
    }

    return -1;
};

補充討論

實作的時候發現
如果再記錄完個單字出現次數後,重新檢查Map值是否有單個單字時,單純跑判斷如果有出現超過1次的單字,紀錄單個單字的index增加,最後要return的時候再判斷目前單個單字的index是否已經超出字串s ,這會跑得比上面的寫法快一點點。(以下方程式範例測試)

比較快的原因,有可能是因為while條件設定不同,這邊設定只有Map單字出現次數大於1的時候才要去增加單個單字的index,但上面的寫法會跑進每個Map裡有的單字。(有待確認)

function firstUniqChar(s: string): number {
    // trying to loop once to record appear times and record the firstIndex
    /**
        set 1 Map
     */
    const charMap = {};
    let firstAppear = 0;
    for(let i=0; i < s.length; i++) {
        const char = s[i];
        if(!charMap[char]) {
            charMap[char] = 0;
        }
        charMap[char]++
    }

    while(charMap[s[firstAppear]]>1) {
        firstAppear++
    }

    return charMap[s[firstAppear]] ? firstAppear : -1;
};

另外有發現leetcode上大神的超神寫法Java 7 lines solution 29ms – First Unique Character in a String – LeetCode

這邊用typescript模擬一下大大的程式碼,大概流程是(範例為下方程式碼):

  • 先創建一個陣列charCount,長度為26,每個index內容都為0(因為要用陣列的index來做為各單字,charCount[index]表示該單字出現的次數初始為0)
  • 遍歷字串s ,來將有出現單字的位置+1(代表這個單字多出現一次了)
  • 遍歷完字串後,會取得所有單字的出現次數
  • 再重新遍歷一次字串s,這次有遇到值為1的就可以return了

大神能想到用charCode的位置來做map紀錄,真的很厲害,雖然看到題目大部分的人可能一開始就想到Map,但可以利用charCode位置來做紀錄,可以知道大神創意的背後是對charCode的機制有一定的理解。

function firstUniqChar(s: string): number {
    const MAX_CHAR = 26;
    const charCount = new Array(MAX_CHAR).fill(0);

    for (let i = 0; i < s.length; i++) {
        const char = s[i];
        const appearPos = char.charCodeAt(0) - "a".charCodeAt(0);
        charCount[appearPos]++
    }

    for (let i = 0; i < s.length; i++) {
        const char = s[i];
        const appearPos = char.charCodeAt(0) - "a".charCodeAt(0);
        if(charCount[appearPos] === 1) {
            return i
        }
    }
    return -1;
};

感謝閱讀,如果有錯誤或是講得不清楚的部分,再請留言指教

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *