leetcode438. 找到字符串中所有字母异位词(附带三种方法复杂分析)

63 阅读3分钟

image.png

1、题目分析

找到字符串中所有字母异位词,通过分析可以得出题目的关键在于如何确定选择的字符串与目标字符串是字母异位词,以及遍历的具体过程

2、思路历程

第一版(复杂度最高):通过排序比较

image.png 判断两个字符串是字母异位词,当时想法是:把字符串转成数组进行处理,对字符串进行排序,如果字符串相等,则说明是字母异位词,遍历的过程是删除第一个指定元素的元素(左边界元素),添加一个新元素(右边界)

比较的方法

for (let i = 0; i <= s.length - length; i++) {
        if (temp === sortP) {
            result.push(i);
        }

转化成字符串的方法

let temp = s.substring(0, length).split("").sort().join("");

首先初始化子字符串: s.substring(0, length)

再转成数组进行排序在连接成字符串,得到初始化后的排序子字符串 temp = s.substring(0, length).split("").sort().join("");

遍历的过程

删除左边界: temp = temp.replace(s[i], '');

添加右边界,通过创建一个遍历插入方法:

    function insertCharInOrder(str, charToInsert) {
        let arr = str.split('');
        let inserted = false;

        for (let i = 0; i < arr.length; i++) {
            if (arr[i] > charToInsert) {
                arr.splice(i, 0, charToInsert);
                inserted = true;
                break;
            }
        }
        if (!inserted) {
            arr.push(charToInsert);
        }
        return arr.join('');
    }

最终压时间线完成:

时间复杂度分析:

O(n * p.length )

第二版(复杂度一般):通过Map对象来比较,新增和删除

image.png 通过Map对象存储26个字符,每个key代表26个字母中的每个字母,value就代表字母的个数, 判断Map对象相同的方法是通过遍历Map对象,删除的方法是hash.get(),如果存在且value为1,则删除这个key,如果存在且大于1,则value--,同样如果新增hash.has()为false,则hash.set(),如果为true则value++

判断Map对象相同的方法

function compareMaps(map1, map2) {
    if (map1.size !== map2.size) {
        return false;
    }
    for (let [key, value] of map1) {
        if (map2.get(key) !== value) {
            return false;
        }
    }
    return true;
}

新增的方式:

 targetHash.set(char, (targetHash.get(char) || 0) + 1);

删除的方式

        if (i >= windowLength) {
            let charToRemove = s[i - windowLength];
            if (targetHash.get(charToRemove) === 1) {
                targetHash.delete(charToRemove);
            } else {
                targetHash.set(charToRemove, targetHash.get(charToRemove) - 1);
            }
        }

第三版(复杂度最低)超过98%:通过创建一维数组存储每一个元素对应的值,索引代表字母的ASCII码

image.png

如何将索引转换成对应的字母ASCII码呢:

// 辅助函数,将字符映射到数组索引
    const charToIndex = char => char.charCodeAt(0) - 'a'.charCodeAt(0);

将字母通过charCodeAt转成ASCII码后减去'a'的ASCII码后即对应数组的index

初始化两个数组:

  // 使用数组来存储字符频率
    let hash = new Array(26).fill(0);
    let targetHash = new Array(26).fill(0);
   for (let i = 0; i < windowLength; i++) {
        hash[charToIndex(p[i])]++;
        targetHash[charToIndex(s[i])]++;
    }
    //虽然叫hash但是是Array类型

滑动过程

    // 滑动窗口
    for (let i = windowLength; i < targetLength; i++) {
        if (arraysAreEqual(hash, targetHash)) {
            result.push(i - windowLength);
        }

        // 更新 targetHash:移除前一个字符,添加新字符
        targetHash[charToIndex(s[i - windowLength])]--;
        targetHash[charToIndex(s[i])]++;
    }

最后再比较最后一个窗口

比较两个数组相同的方法

function arraysAreEqual(arr1, arr2) {
    for (let i = 0; i < 26; i++) {
        if (arr1[i] !== arr2[i]) {
            return false;
        }
    }
    return true;
}

复杂度分析:O(s.length))

总结:

判断两个数组相同的方法能使用数组就使用数组,尽量少使用排序进行,对于不要求顺序的比较,可以通过一维数组索引代表字母,值代表个数的方法来存储,可以有效降低复杂度。