[Leetcode][greedy strategy]45/678

81 阅读2分钟

45.Jump GameII

Description:

You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0].

Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at nums[i], you can jump to any nums[i + j] where:

  • 0 <= j <= nums[i] and
  • i + j < n

Return the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach nums[n - 1].

 

Example 1:

Input: nums = [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.

Solution: Jump Game - II

The solution adopts a greedy strategy, focusing on maximizing reach within the array in the minimum number of jumps.

fun jump1(nums: IntArray): Int {
    // if only have one element, return 0
    if (nums[0] <= 0) {
        return 0
    }
    val n = nums.size - 1
    var current = 0
    var reachMax = 0
    var result = 0
    
    // the last index of the traverse is nums.size - 2
    for (i in 0 until n) {
        reachMax = max(reachMax, i + nums[i])
        // if reachMax great than n,return result + 1, 
        // if nums[0] = 0 , here will return 1, so we need to judge at the beginning of the code.
        if (reachMax >= n) {
            return result + 1
        }
        if (i == current) {
            result++
            current = reachMax
        }
    }
    return result
}

Above solution is really succint and effective. and the below is my initial thought. We record the min step of each position which can reach the end, and we search from current to the max index which current position can reach. the min step to the end is the min step of the position can be reached plus 1:

fun jump(nums: IntArray): Int {
    val res = IntArray(nums.size)
    res[nums.size - 1] = 0
    for (i in nums.size - 2 downTo 0) {
        if (i + nums[i] >= nums.size - 1) {
            res[i] = 1
        } else if (nums[i] == 0) {
            res[i] = Integer.MAX_VALUE
        } else {
            var min = Integer.MAX_VALUE
            for (j in i + 1 ..min(i + nums[i], nums.size - 1)) {
                min = min(res[j], min)
            }
            res[i] = if (min == Integer.MAX_VALUE) Integer.MAX_VALUE else min + 1
        }
    }
    return res[0]
}

678.Valid ParenthesisString

Given a string s containing only three types of characters: '('')' and '*', return true if s is valid.

The following rules define a valid string:

  • Any left parenthesis '(' must have a corresponding right parenthesis ')'.
  • Any right parenthesis ')' must have a corresponding left parenthesis '('.
  • Left parenthesis '(' must go before the corresponding right parenthesis ')'.
  • '*' could be treated as a single right parenthesis ')' or a single left parenthesis '(' or an empty string "".

 

Example 1:

Input: s = "()"
Output: true

Solution: editorial solution

The fisrt thought when i saw the question solving brackets matching problem is using stack. What we should pay attention to is the positions of asterisks and open bracket, for example, "(())**((", the asterisks before open brackets, so they can't cancel each other out. We can use two stacks: one for open brackets and another for the asterisk. Eack stack keeps the indices of open bracket or asterisk.

fun checkValidString(s: String): Boolean {
    val stack = Stack<Int>()
    val asterisk = Stack<Int>()
    s.forEachIndexed{ index, it ->
        if (it == '(') {
            stack.push(index)
        } else if (it == '*') {
            asterisk.push(index)
        } else {
            if (stack.isNotEmpty()) {
                stack.pop()
            } else if (asterisk.isNotEmpty()) {
                asterisk.pop()
            } else {
                return false
            }
        }
    }
    
    // check if there are remaining open brackets and asterisks that can balance them
    while (stack.isNotEmpty() && asterisk.isNotEmpty()) {
        if (stack.pop() > asterisk.pop()) {
            return false
        }
    }
    return stack.isEmpty()
}

However, the official gives another succient solution, using two-pointer greed approach. The approach checks the balance between open and closed brackets from both ends of the array simultaneously, ensuring that no surplus or deficit of brackets occurs at any point during the iteration.

fun checkValidString1(s: String): Boolean {
    var openBracket = 0
    var closeBracket = 0
    for (i in s.indices) {
        // Traverse the string from 0 -> s.length - 1
        val c = s[i]
        // count open parentheses or asterisks
        if (c == '(' || c == '*') {
            openBracket++
        } else {
            openBracket--
        }
        
        // count close parentheses or asterisks
        val c1 = s[s.length - 1 - i]
        if (c1 == ')' || c1 == '*') {
            closeBracket++
        } else {
            closeBracket--
        }
        
        // if at any point open count or close count goes nagative, the string is invalid.
        if (openBracket < 0 || closeBracket < 0) {
            return false
        }
    }
    return true
}

The most important part of this quesiton is how to tackle with the situations "(" and ")".