KMP演算法
例如一個字串有30W個字元判斷是否存在"I am Chinese". 類似這樣的查詢字元的毫無疑問需要使用KMP
.
KMP
演算法由二個部分組成.
- 獲取查詢串的部分匹配表PMT
- 源串根據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;
}
}
複製程式碼
字串壓縮
設計一種方法,通過給重複字元計數來進行基本的字串壓縮。 例如,字串 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
}
複製程式碼
最長無重複字元的子串
給定一個字串,請找出其中無重複字元的最長子字串。 例如,在"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
}
複製程式碼
有效迴文串
給定一個字串,判斷其是否為一個迴文串。只考慮字母和數字,忽略大小寫。 例如"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
}
}
複製程式碼
羅馬數字轉整數
給定一個羅馬數字,將其轉換成整數。 回的結果要求在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
給你兩個字串 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
給定一個字串 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
}
複製程式碼