JavaScript資料結構與演算法(串)

fiveoneLei發表於2018-05-30

KMP演算法

例如一個字串有30W個字元判斷是否存在"I am Chinese". 類似這樣的查詢字元的毫無疑問需要使用KMP. KMP演算法由二個部分組成.

  1. 獲取查詢串的部分匹配表PMT
  2. 源串根據PMT進行回滾 回滾位數 = 已匹配的字元數 - 對應的部分匹配值

PMT

最大匹配數是字首集合與字尾集合的交集中最長元素的長度

   // abababc 
   function PMT(str){
            let next = [], n = 0;
            next[0] = 0;
            for (let i = 1; i < str.length; i++) {
                while (n > 0 && str[i] != str[n]) {  // 當前字元不等於第n+1位字元 那麼n為次大匹配格式再進行判斷
                    n = next[n-1]
                }
                if (str[i] == str[n]) {
                    n++;
                }
                next[i] = n;
            }
            return next;
        }
    }
    PMT("ababa")   => [0, 0, 1, 2, 3]
複製程式碼

while (n > 0 && str[i] != str[n])這段程式碼可能比較難理解. 主要是根據next(n-1)最大匹配數來計算next(n). 如果str[i] == str[n]那麼next[n] = next[n-1]+1, 否則將n設定為次大匹配數next[n-1]; 如果還是理解不了可以看知乎

回滾

如果沒有匹配上,源串會進行回滾回滾位數 = 已匹配的字元數 - 對應的部分匹配值

function KMP(){
    const next = PMT(k);
    let index = -1;
    for (let i = 0; i < str.length; i++) {
        for (let j = 0; j < k.length; j++){
            if (str[i] == k[j]) { // 如果相等
                if (j == k.length-1) {  // 完全匹配                         
                    index = i-k.length+1
                    break;
                }
                i++;
            } else {    
                i = i-j+next[j] 
                break;
            }
        }
    }
    if (index == -1) {
        return false;
    }
}
複製程式碼

字串壓縮

lintcode

設計一種方法,通過給重複字元計數來進行基本的字串壓縮。 例如,字串 aabcccccaaa 可壓縮為 a2b1c5a3 。而如果壓縮後的字元數不小於原始的字元數,則返回原始的字串。

const compress = function (str) {
    if (!str.length) {
        return str;
    }
    let newstr = str[0], num = 1;
       for (let i = 1; i < str.length; i++) {
            if (str[i] == str[i-1]) {
                num++;
                if (i == str.length - 1) {
                    newstr += num
                }
            } else {
                    newstr += num;
                newstr += str[i]
                num =1;
            }
        }
        if (newstr.length >= str.length) {
            return str
        }
        return newstr
}
複製程式碼

最長無重複字元的子串

lintcode

給定一個字串,請找出其中無重複字元的最長子字串。 例如,在"abcabcbb"中,其無重複字元的最長子字串是"abc",其長度為 3。

const lengthOfLongestSubstring = function (str) {
  let max = 0,
        i = 0,
        index = 0,
        hash = {};
    while (i < str.length) {
        let letter = str[i];
        if (!hash[letter]){
            hash[letter] = true;
            if (i-index+1 >= max){
                max = i-index+1
            }
        } else {
            while (index < i) {
                if (str[index] != letter) {
                    hash[str[index]] = false;
                   index++
                } else {
                    index++;
                    break
                }
            }
            hash[letter] = true;
        }
        i++;
    }
    return max
}
複製程式碼

有效迴文串

lintcode

給定一個字串,判斷其是否為一個迴文串。只考慮字母和數字,忽略大小寫。 例如"A man, a plan, a canal: Panama" 是一個迴文。

const isPalindrome = function (str) {
   var i = 0, j = str.length-1;
    if (str.length == 1) {
        return true
    } 
    while (j >= i+1) {
            if (!/[\da-zA-z]/.test(str[i])) {
                i++
            }
            if (!/[\da-zA-z]/.test(str[j])) {
                j--
            }
            if ( j >i && str[i].toLowerCase() != str[j].toLowerCase()) {
                return false
            }
            i++; j--;
        }
        return true
    }
}
複製程式碼

羅馬數字轉整數

lintcode

給定一個羅馬數字,將其轉換成整數。 回的結果要求在1到3999的範圍內。。

const romanToInt = function (str) {
    let map = new Map([["I", 1], ["V", 5], ["X", 10], ["L", 50], ["C", 100], ["D", 500], ["M", 1000]]);
    let total = 0;
    // 左減必須為1位  右減不超過3位
    if (str.length == 1) {
        return map.get(str[0]);
    }
    for (let i = 1; i < str.length; i++) {
        let rightNum = map.get(str[i]);
        let leftNum = map.get(str[i-1]);
        if (rightNum > leftNum) {
            total += (rightNum-leftNum);
            i++;
        } else {
            total += leftNum;
        }
        if (i == str.length-1) {
            total += map.get(str[i]); 
        }
    }
    return total
}
複製程式碼

One Edit Distance

lintcode

給你兩個字串 S 和 T, 判斷他們是否只差一步編輯。 例如字串 s = "aDb", t= "adb"返回true

const isOneEditDistance = function (s, t) {
    if (Math.ceil(s.length-t.length) >= 2){
        return false;
    }
    let len = Math.abs(s.length, t.length);
    let count = 0;  // 調整次數
    for (let i = 0, j = 0; i < len ; i++) {
         if (s[i] != t[j]) {  
            count++;
            if (count >= 2) {
                return false
            }
            if (s.length > t.length) {
                j--
            } else if (s.length < t.length) {
                i--
            }       
        }
        j++;
    }
    if (count == 1 || (count == 0 && Math.abs(s.length-t.length) == 1)) {
        return true;
    } else {
        return false;
    }
}
複製程式碼

Find All Anagrams in a String

lintcode

給定一個字串 s 和一個 非空字串 p ,找到在 s 中所有關於 p 的字謎的起始索引。 字串僅由小寫英文字母組成,字串 s 和 p 的長度不得大於 40,000。 輸出順序無關緊要。

const findAnagrams = (s, p) => {
    let indexs = [],
        storehash = {},
        hash  = {}
    for (let i = 0; i < p.length; i++) {
        let str = p[i];
        storehash[str] = storehash[str] ? storehash[str] + 1 : 1;
        hash[str] = hash[str] ? hash[str]+1:1
    }
    let i = 0,
        index = -1,
        count = 0;
    while (i < s.length) {
        let char = s[i];                               
        if (hash[char]){
            if (index == -1) {
                index = i;
            }
            count++;
            hash[char]--;
            if (count == p.length) {                   // 如果count等於0說明滿足情況 儲存i
                indexs.push(index);
                hash[s[index]]++;
                count = p.length-1;
                index++;
            }

        } else {
            if (index != -1 && hash[char] != undefined && (s.length - index) >= p.length) {
                while (index < i) {  // char溢位了 丟棄前面不為char的字元
                    if (s[index] === char) {
                        index++;
                        break;
                    } else {   
                        count--;
                        hash[s[index]]++;
                    }   
                    index++;
                }
            } else {                                   // 遇到不在hash中的字元則初始化hash, index和count
                hash = Object.assign({}, storehash);
                count = 0;
                index = -1;
            }
        }

        i++;
    }
    return indexs
}
複製程式碼

相關文章