算法-判斷字符迴文

描述

給定非空字符串s,您最多可以刪除一個字符。判斷是否可以成爲迴文。

該字符串僅包含小寫字符a-z,字符串的最大長度爲50000。

樣例:

Given s = "aba" return true

Given s = "abca" return true // delete c

題目分析:

  • 如果單單是迴文的話,就很簡單了:

    s === [...s].reverse().join(""); // 翻轉字符串與原字符相比
    // 實際上這裏做了很多步操作,字符轉數組 翻轉數組 再轉字符串,所以這裏性能也不是很好
    // 如果對性能要求比較高的話,還是通過循環從兩側向中間逐一比較,會更好一點
  • 題目中還有一個要求:刪除一個字符,也就是允許一個字符的不同。

  • 下面我們的解法主體思路就是通過循環,從兩側向中間比較,然後解決當出現不同的情況,如何保證只允許出現一個不同

code:

  1. 出現一處不同 將值傳入一個新函數,再進行判斷字符串:

    const validPalindrome = s => {
        let left = 0;
        let right = s.length - 1;
        while (left < right) {
            if (s[left] !== s[right]) {
                // 左右兩邊字符都要嘗試一下 一邊返回true即可
                return isSubPalindrom(s, left + 1, right) || isSubPalindrom(s, left, right - 1); 
            }
            left++;
            right--;
        }
        return true// 循環結束返回true
    }
    const isSubPalindrom = (s, left, right) => {
        while (left < right) {
            if (s[left] !== s[right]) {
                return false// 再有不同之處 返回false
            }
            left++;
            right--;
        }
        return true// 循環結束一直相等返回true
        // 並且left不小於right 直接返回right,說明不同之處只有一個
    }
    console.log('迴文驗證:', validPalindrome('abaacaaa'), validPalindrome('ab'), validPalindrome('abc'), validPalindrome('aabsjdbaa'))

    這個寫好之後,我在想能不能通過遞歸的形式來解決,爲什麼要創建第二個函數

  2. 遞歸解法:

    const validPalindrome = (s, left = 0, right = s.length - 1, type = 'first') => {
        if (type === 'first') {
            // 第一次進入允許出現一次不同
            while (left < right) {
                if (s[left] !== s[right]) {
                    return validPalindrome(s, left + 1, right, 'second') || validPalindrome(s, left, right - 1'second'); // 左右兩邊都嘗試一下 一邊返回true即可
                }
                left++;
                right--;
            }
            return true// 循環結束返回true
        } else {
            // 第二次 再有不同之處 返回false
            while (left < right) {
                if (s[left] !== s[right]) {
                    return false
                }
                left++;
                right--;
            }
            return true// 循環結束一直相等返回true
            // 並且left不小於right 直接返回right,說明不同之處只有一個
        }
    }
    console.log('迴文驗證:', validPalindrome('abaacaaa'), validPalindrome('ab'), validPalindrome('abc'), validPalindrome('aabsjdbaa'))

    相對於上個解法這裏就是多設置了一個變量,然後將兩方區分開來,但是這樣遞歸我還是覺得太傻了,所以在想你能不能通過設置變量來處理?出現兩次不同即失敗。

  3. 設置一個變量允許一次不同

    const validPalindrome = s => {
        let removed = false;
        for (let [i, j] = [0, s.length - 1]; i < j; i++ , j--) {
            // 從兩側向中間遞減 i- j-1 減到最後 i>j i=j 退出循環
            if (s[i] !== s[j]) { // 如果兩側不相同
                if (removed) { // 只允許一次不同
                    return false;
                }
                if (s[i] === s[j - 1]) {
                    // 轉數組刪除一個不同元素 下次直接return
                    // s = [...s].splice(j, 1);
                    // s = s.join(''); // 處理過的字符串
                    j--;
                    removed = true;
                } else if (s[i + 1] === s[j]) {
                    // s = [...s].splice(i, 1);
                    // s = s.join(''); // 處理過的字符串
                    i++;
                    removed = true;
                } else {
                    // 上面兩個else 右邊-1 或左邊+1相不相等 如果兩邊也不相等即false
                    return false;
                }
            }
        }
        return true;
    }
    console.log('迴文驗證:', validPalindrome('abaacaaa'), validPalindrome('ab'), validPalindrome('abc'), validPalindrome('aabsjdbaa'))

代碼地址

github 算法倉庫地址

2018.8.12


本文分享自微信公衆號 - OBKoro1前端進階積累(gh_8af2fb8e54a9)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章