diff --git a/content/posts/leetcode.md b/content/posts/leetcode.md index 4df179c..d2b00d3 100644 --- a/content/posts/leetcode.md +++ b/content/posts/leetcode.md @@ -2643,3 +2643,497 @@ func isIsomorphic(s string, t string) bool { return true } ``` + +## day36 2024-04-03 + +### 79. Word Search + +Given an m x n grid of characters board and a string word, return true if word exists in the grid. + +The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once. +![0403PbwhJmQsvAC8](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0403PbwhJmQsvAC8.png) + +### 题解 + +本题可使用递归求解, 设置一个标记数组和board大小相同, 来标记已经访问过的元素, 遍历board数组, 找到目标字符串的起始字符, 对其调用explore函数求解, 若为true则直接返回true. 最后全部遍历完则返回false. + +explore函数中, 传入了当前字符的行下标和列下标, 先判断可行的探索方向, 再在可行的探索方向上逐个判断其是否为目标字符串中的下一个字符以及是否已经被访问过, 若可以访问且为目标字符, 则对这个可行方向的字符调用explore函数继续后面的探索, 知道探索到目标字符串结尾则直接返回true. 若调用explore函数返回为true说明后面的字符串符合目标字符串且有可行路径, 则返回true. 若没有可探索方向或者所有可探索方向都不满足条件, 返回false. + +使用递归要考虑的是在当前递归状态中应该完成什么任务以及递归的结束条件, 递归本质上是一种将全局问题化解为小的局部问题, 通过求解每个小的局部问题最终解决全局问题的解决问题的思路. + +### 代码 + +```go + +func exist(board [][]byte, word string) bool { + for index, value := range board{ + for index1, value2 := range value{ + if value2 == byte(word[0]){ + var flag [][]int + for _,_ = range board{ + a := make([]int, len(board[0])) + flag = append(flag, a) + } + if (explore(board, index, index1, 0, flag, word)){ + return true + } + } + } + } + return false + +} + +func explore(board [][]byte, row int, column int, target int, flag [][]int, word string)bool{ + if target == len(word)-1 && byte(word[target]) == board[row][column]{ + return true + } + up, down, left, right := !(row == 0),!(row == len(board)-1),!(column == 0), !(column == len(board[0])-1) + ok := false + if up && board[row-1][column] == byte(word[target+1])&&(flag[row-1][column] == 0){ + if target+1 == len(word) - 1{ + return true + } + flag[row][column] = 1 + ok = ok || explore(board, row-1, column, target+1, flag,word) + flag[row][column] = 0 + } + if down && board[row+1][column] == byte(word[target+1]) &&(flag[row+1][column] == 0){ + if target+1 == len(word) - 1{ + return true + } + flag[row][column] = 1 + ok =ok || explore(board, row+1, column, target+1, flag, word) + flag[row][column] = 0 + } + if left && board[row][column-1] == byte(word[target+1]) &&(flag[row][column-1] == 0){ + if target+1 == len(word) - 1{ + return true + } + flag[row][column] = 1 + ok =ok || explore(board, row, column-1, target+1, flag, word) + flag[row][column] = 0 + } + if right && board[row][column+1] == byte(word[target+1]) &&(flag[row][column+1] == 0){ + if target+1 == len(word) - 1{ + return true + } + flag[row][column] = 1 + ok = ok || explore(board, row, column+1, target+1, flag, word) + flag[row][column] = 0 + } + return ok +} + +``` + +## day37 2024-04-04 + +### 1614. Maximum Nesting Depth of the Parentheses + +A string is a valid parentheses string (denoted VPS) if it meets one of the following: + +It is an empty string "", or a single character not equal to "(" or ")", +It can be written as AB (A concatenated with B), where A and B are VPS's, or +It can be written as (A), where A is a VPS. +We can similarly define the nesting depth depth(S) of any VPS S as follows: + +depth("") = 0 + +depth(C) = 0, where C is a string with a single character not equal to "(" or ")". + +depth(A + B) = max(depth(A), depth(B)), where A and B are VPS's. + +depth("(" + A + ")") = 1 + depth(A), where A is a VPS. + +For example, "", "()()", and "()(()())" are VPS's (with nesting depths 0, 1, and 2), and ")(" and "(()" are not VPS's. + +Given a VPS represented as string s, return the nesting depth of s. + +![0404oW0NMjd6UQ2W](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0404oW0NMjd6UQ2W.png) + +### 题解 + +本题是简单题, 因为题目说明了给定的括号一定是匹配的, 因此不用考虑括号匹配的问题. 只需设计一个值来保存当前还未匹配的左括号的个数, 这也是到当前这个字符的括号深度, 因此若出现了新的左括号, 就更新最大深度为当前的左括号个数和之前的最大深度中的较大值, 出现右括号则将未匹配的左括号个数减一, 最后返回最大深度即可. + +### 代码 + +```go +func maxDepth(s string) int { + depth := 0 + left := 0 + for _,value := range s{ + if value == '('{ + left++ + depth = max(depth, left) + } + if value == ')'{ + left-- + } + } + return depth +} +``` + +## day38 2024-04-05 + +### 1544. Make The String Great + +Given a string s of lower and upper case English letters. + +A good string is a string which doesn't have two adjacent characters s[i] and s[i + 1] where: + +0 <= i <= s.length - 2 +s[i] is a lower-case letter and s[i + 1] is the same letter but in upper-case or vice-versa. +To make the string good, you can choose two adjacent characters that make the string bad and remove them. You can keep doing this until the string becomes good. + +Return the string after making it good. The answer is guaranteed to be unique under the given constraints. + +Notice that an empty string is also good. + +![040507vRG8OFjjEa](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/040507vRG8OFjjEa.png) + +### 题解 + +本题可以使用类似简单回溯的方法, 遍历字符串并判断当前字符和下一个字符是否互为大小写, 若是则删掉两个字符同时将循环下标回退到当前字符的前一个, 继续遍历字符串, 如此反复直到到达字符串结尾即可, 注意处理字符位于字符串结尾和开始的边界情况. + +### 代码 + +```go +func makeGood(s string) string { + index := 0 + length := len(s) + for index=0;index 1 { + + for i, _ := range s { + + n := len(stack) + + if n > 0 && ((s[i]-stack[n-1] == 32) || (stack[n-1]-s[i] == 32)) { + + stack = stack[:n-1] + + } else { + stack = append(stack, s[i]) + } + } + return string(stack) + + } else { + return s + } + +} +``` + +## day39 2024-04-06 + +### 1249. Minimum Remove to Make Valid parentheses + +Given a string s of '(' , ')' and lowercase English characters. + +Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting parentheses string is valid and return any valid string. + +Formally, a parentheses string is valid if and only if: + +It is the empty string, contains only lowercase characters, or +It can be written as AB (A concatenated with B), where A and B are valid strings, or +It can be written as (A), where A is a valid string. + +![04066vTKpXK0w9Dc](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/04066vTKpXK0w9Dc.png) + +### 题解 + +本题实际上还是一个括号匹配的问题, 使用一个栈就可以解决问题, 与前一天题不同的是, 本题中的左括号不一定能完全被匹配, 所以栈中可以保存左括号的下标, 遍历到字符串末尾后, 将栈中仍然存在的未匹配的左括号全部删去即可. + +### 代码 + +```go +func minRemoveToMakeValid(s string) string { + stack := []int{} + number := 0 + index := 0 + for index < len(s){ + if s[index] == ')' { + if number == 0{ + if index == len(s) - 1{ + s = s[0:index] + return s + }else{ + s = s[0:index] + s[index+1:] + } + }else{ + number-- + stack = stack[0:len(stack)-1] + index++ + } + }else if s[index] == '('{ + number++ + stack = append(stack, index) + index++ + }else{ + index++ + } + + } + if len(stack) != 0{ + for index,value := range stack{ + if value - index != len(s) - 1{ + s = s[0:value-index]+s[value+1-index:] + }else{ + s = s[0:value-index] + return s + } + + } + } + return s +} +``` + +## day40 2024-04-07 + +### 678. Valid Parenthesis String + +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 "". + +![0407zQ0ujSVhFg50](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0407zQ0ujSVhFg50.png) + +### 题解 + +本题仍然是括号匹配问题, 只是这次加了一个万能字符'\*',相当于赖子,既可以当'('用也可以当')'用. 作为一个判定问题, 只需要判断字符串是否匹配即可. 因此使用一个变量记录当前未匹配的左括号个数和可以被匹配的星号个数, 当遇到未匹配的右括号时如果没有未匹配的左括号则使用星号匹配. 若遍历结束左括号没有剩余则匹配成功. 若星号个数比左括号少则直接匹配失败. 若左括号和星号仍有剩余, 且星号个数大于左括号时, 说明剩余的星号可能能完全匹配左括号,这时反向遍历,把')'作为之前的左括号看待,用同样的方法尝试匹配所有'('. 最后只要'('能匹配成功即成功. p.s: 这里考虑的是正向遍历时若')'都匹配成功了, 那么只需要测试'('能否匹配就够了, 因为剩余的')'在正向遍历时已经确认可以通过多余的'\*'来完全匹配, 因此不用再考虑')'的匹配问题. + +### 代码 + +```go +func checkValidString(s string) bool { + leftstack := 0 + star := 0 + for _, value := range s { + if value == '*'{ + star++ + }else if value == '('{ + leftstack++ + }else{ + if leftstack > 0{ + leftstack-- + }else if star > 0{ + star-- + }else{ + return false + } + } + } + if leftstack == 0{ + return true + }else if star < leftstack{ + return false + }else{ + leftstack = 0 + star = 0 + for index:= len(s)-1;index>=0;index--{ + if s[index] == ')'{ + leftstack++ + }else if s[index] == '*'{ + star++ + }else{ + if leftstack > 0{ + leftstack-- + }else if star > 0{ + star-- + }else{ + return false + } + } + } + } + return true +} +``` + +## day41 2024-04-08 + +### 1700. Number of Students Unable to Eat Lunch + +The school cafeteria offers circular and square sandwiches at lunch break, referred to by numbers 0 and 1 respectively. All students stand in a queue. Each student either prefers square or circular sandwiches. + +The number of sandwiches in the cafeteria is equal to the number of students. The sandwiches are placed in a stack. At each step: + +If the student at the front of the queue prefers the sandwich on the top of the stack, they will take it and leave the queue. + +Otherwise, they will leave it and go to the queue's end. + +This continues until none of the queue students want to take the top sandwich and are thus unable to eat. + +You are given two integer arrays students and sandwiches where sandwiches[i] is the type of the ith sandwich in the stack (i = 0 is the top of the stack) and students[j] is the preference of the jth student in the initial queue (j = 0 is the front of the queue). Return the number of students that are unable to eat. + +![04085IjBuW1NxyMu](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/04085IjBuW1NxyMu.png) + +### 题解 + +本题是一道简单题, 题干看似很长, 分析下来会发现, 若当前栈顶的三明治当前学生不喜欢, 就会向下轮换, 直到轮换到喜欢的学生, 因此栈顶三明治是否会被拿走取决于队伍中是否还有喜欢这种三明治的学生, 只要有这个学生总会被轮换到队伍的最前面. 则遍历学生数组, 统计两种三明治的喜欢的学生的数量. 遍历三明治数组, 遍历过程中减掉被拿走的三明治的类型喜欢的学生的数量, 直到三明治数组的当前元素没有学生喜欢, 则剩余的三明治就无法被拿到, 也就是无法吃到三明治的学生个数. + +### 代码 + +```go +func countStudents(students []int, sandwiches []int) int { + love1 := 0 + love0 := 0 + length := len(sandwiches) + for _,value := range students{ + if value == 1{ + love1++ + }else{ + love0++ + } + } + for index,value := range sandwiches{ + if value == 1{ + if love1 <= 0{ + return length - index + } + love1-- + }else{ + if love0 <= 0{ + return length - index + } + love0-- + } + } + return 0 +} +``` + +## day42 2024-04-09 + +### 2073. Time Needed to Buy Tickets + +There are n people in a line queuing to buy tickets, where the 0th person is at the front of the line and the (n - 1)th person is at the back of the line. + +You are given a 0-indexed integer array tickets of length n where the number of tickets that the ith person would like to buy is tickets[i]. + +Each person takes exactly 1 second to buy a ticket. A person can only buy 1 ticket at a time and has to go back to the end of the line (which happens instantaneously) in order to buy more tickets. If a person does not have any tickets left to buy, the person will leave the line. + +Return the time taken for the person at position k (0-indexed) to finish buying tickets. + +![0409yxrIrQga6aAr](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0409yxrIrQga6aAr.png) + +### 题解 + +本题是一道简单题,并不需要去考虑模拟队列一轮一轮买票的过程, 而考虑队伍中的每个人在目标k买完票之前买了多少次票并将每个人中买的次数加和即可. 这里要将k之前和k之后的分开考虑, k之前的如果比k的需求大则在k买完之前都要买k需求的票的数量, 比k小则将自己的需求数量买完即可. 在k之后不同在于需求数大于等于k的人只会买k-1张票, 在k买完自己需要的票的时候这些人还在k的后面故当k买完时这些人最多买了k-1张. 遍历一遍数组按照这个规则将每个人的买票数加和即可得到最终结果. + +### 代码 + +```go +func timeRequiredToBuy(tickets []int, k int) int { + target := tickets[k] + time := 0 + for _,value := range tickets[0:k]{ + if value >= target{ + time += target + }else{ + time += value + } + } + for _,value := range tickets[k:]{ + if value >= target - 1{ + time += target - 1 + }else{ + time += value + } + } + // add itself once + time++ + return time +} +``` + +## day43 2024-04-10 + +### 950. Reveal Cards In Increasing Order + +You are given an integer array deck. There is a deck of cards where every card has a unique integer. The integer on the ith card is deck[i]. + +You can order the deck in any order you want. Initially, all the cards start face down (unrevealed) in one deck. + +You will do the following steps repeatedly until all cards are revealed: + +Take the top card of the deck, reveal it, and take it out of the deck. +If there are still cards in the deck then put the next top card of the deck at the bottom of the deck. +If there are still unrevealed cards, go back to step 1. Otherwise, stop. +Return an ordering of the deck that would reveal the cards in increasing order. + +Note that the first entry in the answer is considered to be the top of the deck. + +![0410kWPCSmxbhxXZ](https://testingcf.jsdelivr.net/gh/game-loader/picbase@master/uPic/0410kWPCSmxbhxXZ.png) + +### 题解 + +本题为中等难度, 题目中的不变的为牌的数量, 目标即为将牌按照合适的顺序插入这个牌堆, 首先排序, 然后模拟取牌顺序将牌插入. 用一个数组保存当前牌堆中的可用位置. 设置可用位置为一个队列, 对于需要跳过的位置(即翻一张牌后下一张牌需要放到底部, 相当于跳过了这张牌)将其从队列头放到队列尾, 取出队列下一个位置并将当前牌插入, 再跳过一个位置, 插入一张牌, 如此重复直到清空队列. + +### 代码 + +```go +func deckRevealedIncreasing(deck []int) []int { + position := []int{} + sort.Ints(deck) + now := 0 + result := make([]int,len(deck)) + for index,_ := range deck{ + position = append(position, index) + } + for len(position) > 1{ + result[position[0]] = deck[now] + position = append(position,position[1]) + position = position[2:] + now++ + } + result[position[0]] = deck[now] + return result +} +``` + +### 总结 + +这样虽然写起来方便, 但实际上在不断append的过程中position会占用更多的空间, 使用循环队列可以避免空间的浪费.