leetcode-字符串算法1

// 算法
// 排列组合 - 称砝码
let weightArr = [1, 2]
let numsArr = [2, 1]
let allWeightArr = []

numsArr.forEach((n, i) => {
    allWeightArr = allWeightArr.concat(new Array(n).fill(weightArr[i])) 
})
console.log(allWeightArr)
let weightSumsArr = [] // 几种组合

let result = [0] //几种重量

// 遍历每一个, 跟组合好的数组重新组合形成新的数组,放入新数组
allWeightArr.forEach((w) => {
    let newArr = []
    weightSumsArr.forEach((item) => {
        let arr = []
        arr = [...item, w]
        weightSumsArr.push(arr)
    })
    weightSumsArr.push([w])
})

allWeightArr.forEach((w) => {
    let newArr = []
    result.forEach((item) => {
        newArr.push(w + item)
    })
    result = [...new Set([...result, ...newArr])]
})
console.log(result, result.length)
/*

weightSumsArr = allWeightArr.reduce((pre, w) => {
    pre.forEach(item => {
        let arr2 = [...item, w]
        pre.push(arr2)
    })
    pre.push([w])
    return pre
}, [])
console.log(weightSumsArr)

*/

console.log(weightSumsArr)


//----------------------------面试题 08.08. 有重复字符串的排列组合-----------------------
//----------------------------面试题 08.07. 无重复字符串的排列组合-----------------------
// 全排列 str = 'abc' 思路: 取出第一个,剩下的全排列,然后插入全排列的每一项。递归 + 回溯
function func (str) {
    
    let result = []
    
    if (str.length === 1 || str.length === 0) {
        result.push(str)
        return  [...new Set(result)]
    }
    let one = str[0]
    let other = str.slice(1)
    
    // 取出第一个, 剩下的全排列 ['bc', 'cb']
    let otherCopmose = func(other)
    
    // 然后插入a, 有 3种方式  'abc' 'bac' 'cba'
    for(let i=0; i<otherCopmose.length; i++) {
        // 每次插入,都有 每组长度 + 1 中可能
        for(let j=0; j< otherCopmose[i].length + 1; j++) {
            let arr = otherCopmose[i].split('')
            arr.splice(j, 0, one)
            let newStr = arr.join('')
            result.push(newStr)
        }
    }
    return [...new Set(result)]
}
func('abc')

var func = function (str) {
    let result = []
    
    function search(path) {
        if (path.length === str.length) {
            result.push(path)
        }
        for (let char of str) {
            if (path.indexOf(char) == -1) {
                search(`${char}${path}`)
            }
            
        }
    }
    search('')
    return result
}
func('abc')


//-----------------------------------剑指 Offer II 017. 含有所有字符的最短字符串  难度:困难---------------------
//输入:s = "ADOBECODEBANC", t = "ABC"
//输出:"BANC" 
//"acbbaca"
//"aba"
var minWindow = function(s, t) {
    let result = []
    if (s.length < t.length) return ''
    let tmap = {}
    for (let ts of t) {
        tmap[ts] = tmap[ts] ? tmap[ts] + 1 : 1
    }

    for (let i=0;i<s.length;i++) {
        for (let j=i+1; j<=s.length; j++) {
            let ss = s.slice(i, j)
            let ssmap = {}
            for (let sss of ss) {
                ssmap[sss] = ssmap[sss] ? ssmap[sss] + 1 : 1
            }
            let flag = true
            for (let tss of t) {
                if (tmap[tss] > ssmap[tss] || !ss.includes(tss) ) {
                    flag = false
                    break
                }
            }
            if (flag && ss.length >= t.length) {
                result.push(ss)
            }
        }
    }
    // console.log(result)


    let sortSubs = result.sort((a, b) => a.length - b.length)
    if (sortSubs.length == 0) return ''
    return sortSubs[0]
    // console.log(sortSubs)

};

//-------------------字符串所有子串 - 递归---------------------------
var strFromSubStr = function (str) {
    let result = []
    
    function dfs (s) {
        if (s.length === 1) {
            result.push(s)
            return result
        }
        let one = s[0]
        let subs = dfs(s.slice(1))
        subs.forEach(ss => {
            result.push(one, one + ss)
        })
        return result
     }
    dfs(str)
    return [...new Set(result)]
}


//---------------------------剑指 Offer II 015. 字符串中的所有变位词-------------------------------
//输入: s = "cbaebabacd", p = "abc"
//输出: [0,6]
var findAnagrams = function(s, p) {
    let res = []
    let pmap = {}
    for (let c of p) {
        pmap[c] = pmap[c] ? pmap[c] + 1 : 1
    }

    for(let i=0; i<s.length; i++) {
        let target = s.slice(i, i+p.length)
        // target 和 p 是变位词
        if (target.length !== p.length) continue
        let tmap = {}
        for (let c of target) {
            tmap[c] = tmap[c] ? tmap[c] + 1 : 1
        }
        let flag = true
        let keys = Object.keys(tmap)
        for (let j=0; j<keys.length; j++) {
            if (tmap[keys[j]] !== pmap[keys[j]]) {
                flag = false
                break
            }
        }
        if (!flag) continue

        res.push(i)
    }
    return res
};

//-----------------------剑指 Offer II 014. 字符串中的变位词: s1 是否是 s2 的变位词---------------
var checkInclusion = function(s1, s2) {
    for(let i=0; i<s2.length; i++) {
        let ts = s2.slice(i, i+s1.length)
        if (ts.length !== s1.length) continue
        if (ts.split('').sort().join('') === s1.split('').sort().join('')) {
            return true
        } else {
            continue
        }
    }
    return false
};
// 双指针解法
var checkInclusion = function(s1, s2) {
    let need = new Array(26).fill(0)
    let len1 = s1.length
    let len2 = s2.length
    
    for (let s of s1) {
        let i = s.charCodeAt() - 97
        need[i] += 1
    }

    // console.log(need)
    
    let l=0, r=0;
    let all = new Array(26).fill(0)
    
    while (r<len2) {
        all[s2[r++].charCodeAt() - 97] += 1
        
        if (r-l === len1) {
            
            if (need.toString() === all.toString()) {
                console.log(all)
                return true
            }
            all[s2[l++].charCodeAt() - 97] -= 1
        }
        
    }
    
    return false
    
}

//-------------------剑指 Offer II 086. 分割回文子字符串----------------------
// 输入: 'google'
// 输出: [ ["g", "o", "o", "g", "l", "e"],["g", "oo", "g", "l", "e"], ["goog", "l", "e"] ]
var partition = function(s) {
    let res = []
    let arr = []
    function search (index) {
        if (index === s.length) {
            return res.push(arr)

        }
        let target = ''
        for (let i=index; i<s.length; i++) {
            target += s[i]
            if (target !== target.split('').reverse().join('')) continue
            arr.push(target)
            search(i + 1)
            arr.pop()
        }
        
    }
    search(0)
    return res
};


// ------------------最长无重复子串----------------------------
var lengthOfLongestSubstring = function(s) {
    let max = 0
    let left = 0
    let right = 1
    let l = 0
    let r = 0
    if (s.length === 0 || s.length === 1) return s.length
    while (right < s.length) {
        let sub = s.slice(left, right)
        if (sub.indexOf(s[right]) > -1) {
            left++
            continue
        } else {
            right++
        }
        if (right - left > max) {
            max = right - left
            l = left
            r = right
        }
    }
    console.log(s.slice(l, r))
    return s.slice(l, r).length
};

//---------------------------------------516. 最长回文子序列-------------
var longestPalindromeSubseq = function(s) {
    let len = s.length
    let dp = Array.from(new Array(len),()=>new Array(len).fill(0))
    for(let i = len-1;i>=0;i--){
        dp[i][i] = 1
        for(let j = i+1;j<len;j++){
            if(s[i]==s[j]){
                // i到j  满足回文的话,去掉第一个和最后一个字符依然是回文。 i+1 去掉首,j-1 去掉尾。 去掉了两个字符,所以长度是 +2
                dp[i][j] = dp[i+1][j-1] +2
            }else{
                // i到j 不满足的话,取 i+1 ~ j 和 i~j-1 最长的
                dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1])
            }
        }
    }
    return dp[0][len-1]
};

// -----------------------leetcode 8.字符串转整数 --------------------------
var myAtoi = function(str) {
    //利用正则
    let result = str.trim().match(/^[-|+]{0,1}[0-9]+/)
    if(result != null){
        if(result[0] > (Math.pow(2,31) -1)){
            return Math.pow(2,31) -1
        }
        if(result[0] < Math.pow(-2,31)){
            return Math.pow(-2,31)
        }

        return result[0]
    }
    
    return 0
};

//------------------------leetcode  205. 同构字符串:  哈希表----------------------------
// 输入:s = "egg", t = "add"   正向:e->a  g->d  反向:a->e  d->g
// 输出:true
var isIsomorphic = function(s, t) {
    
    function helper (str1, str2) {
        let map = {}
        let len = str1.length
        for (let i=0; i<len; i++) {
            let s1 = str1[i]
            let t1 = str2[i]
            if (map[s1]) {
                if (map[s1] !== t1) {
                    return false
                }
            } else {
                map[s1] = t1
            }
        }
        console.log(map)
        return true
    }

    return helper(s, t) && helper(t, s)
};

//-------------------------leetcode 43 字符串数字相乘, 大数相乘---------------
var multiply = function(num1, num2) {
    if (num1 === '0' || num2 === '0') {
        return '0'
    }
    let arr1 = num1.split('').reverse()
    let arr2 = num2.split('').reverse()
    let L1 = arr1.length
    let L2 = arr2.length
    let result = new Array(L1+L2).fill(0)

    for (let i=0; i<L1; i++) {
        for(let j=0; j<L2; j++) {
            let carryIndex = i + j + 1  //进位位置
            let curIndex = i + j        //当前位置
            let sum = arr1[i] * arr2[j] + result[curIndex]  // 当前的和 = 当前的乘积 + 当前位置的值(上次进位的值)
            result[curIndex] = sum % 10  //当前位置的值
            result[carryIndex] += Math.floor(sum / 10)  // 进位位置的值 = 进位位置原来的值 + sum / 10 取整
        }
    }
    console.log(result)
    let res = result.reverse()
    if (res[0] === 0) res.shift() 
    return res.join('')

};

// ---------------------------------------------------动态规划类问题:1. 计数, 2. 最值, 3. 是否存在-------------------------------------------------------
/**
    1.确定状态- 最后一步 + 子问题
    2.总结方程
    3.边界条件
    4.计算顺序
*/
案例1:有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多,买一本书需要27元,如何用最少的硬币组合正好付清,不需要对方找钱
// 分析:1. 最后一步 27-ak

let coins = [2, 5, 7]
function coinsChange (coins, sum) {
    let f = new Array(sum + 1).fill(0)
    
    f[0] = 0;
    
    // 从 f(1) 开始计算一直到 f(27)
    for (let i=1; i<=sum; i++) {
        
        f[i] = Number.POSITIVE_INFINITY
        
        //f[X]=min{f[X-2]+1,f[X-5]+1,f[X-7]+1}
        for (let j = 0; j<coins.length; j++) {
            
            if (i >= coins[j] && f[i - coins[j]] !== Number.POSITIVE_INFINITY) {
                // 取上一次的最小 + 当前
                f[i] = Math.min(f[i], f[i-coins[j]] + 1)
            }
        }
    }
    
    if(f[sum] == Number.POSITIVE_INFINITY) {
        return -1
    }
    return f[sum]
}
coinsChange([2,5,7], 27)

// dp数组优化双重循环带来的重复计算
var coinsChange = (coins, N) => {
    let dp = new Array(N+1).fill(Infinity);
    dp[0] = 0
    if (N < 0) return -1
    for(let i=0; i<dp.length; i++) {
        for(let j=0; j<coins.length; j++) {
            if (i-coins[j] < 0) continue
            dp[i] = Math.min(dp[i], 1 + dp[i-coins[j]])
        }
    }
    return dp[N]
}
coinsChange([2,5,7], 27)


// 案例2: 给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下或者向右走一步, 有多少种不同的方式走到右下角

function skipGEZI (m, n) {
    let f = new Array(m).fill(new Array(n).fill(0))
    
    
    for (let i=0; i<m; i++) {
        for (let j=0; j<n; j++) {
            if(j === 0 || i === 0) {
                f[i][j] = 1
            } else {
                f[i][j] = f[i-1][j] + f[i][j-1]
            }
        }
    }
    return f[m-1][n-1]
}

// 案例3:给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个下标
// 1. 最后一步 a[i] + i >= j   j代表某个位置

const jumpCan = (arr) => {
    let len = arr.length
    let f = new Array(len).fill(false)
    f[0] = true
    
    for (let i=0; i< len; i++) {
        for (let j=0; j<i; j++) {
            if (f[j] && arr[j] + j >= i) {
                f[i] = true
            }
        }
    }
    return f[len -1]
}
jumpCan([2,3,1,1,4]) // true
jumpCan([3,2,1,0,4]) // false


//------------------------------------------leetcode 97.交错字符串------------------------
// 输入:s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
// 输出:true  下面解法不对,应该用动态规划。
var isInterleave = function(s1, s2, s3) {
    let arr1 = s1.split('')
    let arr2 = s2.split('')
    let all = [...arr1, ...arr2]
    
    if (all.length !== s3.length) return false
    
    let allmap = all.reduce((map, cur) => {
        if (map[cur]) {
            map[cur] += 1
        } else {
            map[cur] = 1
        }
        return map
    }, {})
    
    let flag = true
    let map = {}
    for (let ch of s3) {
        if (!all.includes(ch)) {
            flag = false
            break
        }
        if (map[ch]) {
            map[ch] += 1
        } else {
            map[ch] = 1
        }
    }
    
    let keys = Object.keys(allmap)
    for (let j=0; j<keys.length; j++) {
        if (allmap[j] !== map[j]) {
            flag = false
            break
        }
    }
    
    function swrapArr (ar1, ar2) {
        // return ar1.filter((v) => !ar2.includes(v))
        let _ar1 = [...ar1]
        for (let c of ar2) {
            let index = _ar1.findIndex(v => v===c)
            if (index > -1) {
                _ar1.splice(index, 1)
            }

        }
        return _ar1
    }
    
    let s3arr = s3.split('')
    let _arr1 = swrapArr(s3arr, arr2)  // 剩余的arr1
    let _arr2 = swrapArr(s3arr, arr1)  // 剩余的arr2
    console.log(s3arr)
    console.log(_arr1, arr1)
    console.log(_arr2, arr2)
    if (arr1.sort().join('') !== _arr1.sort().join('')) {
        flag = false
    }
    
    if (arr2.sort().join('') !== _arr2.sort().join('')) {
        flag = false
    }
    
    return flag
};

// 正确解法 - 剑指 Offer II 096. 字符串交织
var isInterleave = function(s1, s2, s3) {
    const n = s1.length;
    const m = s2.length;
    if(n+m!=s3.length) return false;
    // dp 路径
    let dp = new Array(n+1).fill(0).map(()=>new Array(m+1).fill(false));
    // dp[i][j] : 长度为[i+j]的s3前缀 能否由 长度为i的s1前缀 与 长度为j的s2前缀 交织组成
    // 先处理一下 i/j 取0 的情况
    dp[0][0] = true;
    for(let i=1;i<n+1;i++) {
        if(s1[i-1]==s3[i-1]) dp[i][0] = true;
        else break;
    }
    for(let j=1;j<m+1;j++) {
        if(s2[j-1]==s3[j-1]) dp[0][j] = true;
        else break;
    }
    for(let i=1;i<n+1;i++) {
        for(let j=1;j<m+1;j++) {
            dp[i][j] = (s1[i-1] == s3[i+j-1] && dp[i-1][j]) || (s2[j-1] == s3[i+j-1] && dp[i][j-1])
        }
    }
    return dp[n][m];
};

//-------------------------97. 交错字符串/ 剑指 Offer II 096. 字符串交织:  s3 是否是由 s1 和 s2 交错 组成 -------------------------------
//dp[i][j] 表示 s1.substring(0, i) 和 s2.substring(0, j) 能交错组成 s3.substring(0, i+j)
//dp[0][0] = true
//dp[i][j] = (dp[i-1][j] && s1[i-1] === s3[i+j-1]) || (dp[i][j-1] && s2[j-1] === s3[i+j-1]);
var isInterleave = function(s1, s2, s3) {
    const m = s1.length + 1, n = s2.length + 1;
    if (s3.length !== m + n - 2) return false;
    const dp = [];
    for  (let i = 0; i < m; ++i) {
        const temp = new Array(n);
        dp.push(temp);
    }
    dp[0][0] = true;
    for (let i = 1; i < m; ++i) {
        dp[i][0] = dp[i-1][0] && s1[i-1] === s3[i-1];
    }
    for (let j = 1; j < n; ++j) {
        dp[0][j] = dp[0][j-1] && s2[j-1] === s3[j-1];
    }
    for (let i = 1; i < m; ++i) {
        for (let j = 1; j < n; ++j) {
            dp[i][j] = (dp[i-1][j] && s1[i-1] === s3[i+j-1]) || (dp[i][j-1] && s2[j-1] === s3[i+j-1]);
        }
    }
    return dp[m-1][n-1];
};







// 案例4:斐波那切数列 dp数组迭代法
function fib(n) {
    let dp = new Array(n+1)
    dp[0] = dp[1] = 1
    for (let i=3; i<=n; i++) {
        dp[i] = dp[i-1] + dp[i-2]
    }
    return dp[n]
}

//---------------爬楼梯 每次1级2级 有多少种方法-------------------
var climbStairs = function(n) {
    let dp = new Array(n+1).fill(0)
    if (n === 1 || n === 2) {
        return n
    }
    dp[0] = 0
    dp[1] = 1
    dp[2] = 2
    for (let i=3; i<=n; i++) {
        dp[i] = dp[i-1] + dp[i-2]
    }
    console.log(dp)
    return dp[n]
};


//----------------------------面试题 05.02. 二进制小数数转字符串----------------------
var printBin = function(num) {
    let dist = []
    while (num) {
        num *=2
        let d = num >= 1 ? 1 : 0
        dist.push(d)
        if (dist.length > 32) return 'ERROR'
        num -= d
    }
    return `0.${dist.join('')}`
};

 

posted @ 2022-08-03 15:53  monkey-K  阅读(17)  评论(0编辑  收藏  举报