leetcode困难题

找到矩阵中的好子集

解:
答案至多选取两行矩阵。
分析:
选取一行矩阵时,floor(c/2)=0,那么必须全为0才能满足。
选取两行矩阵时,floor(c/2)=1,同一列元素不能都是1,也就是相&为0
选取三行矩阵时,floor(c/2)=1,去掉一列后,等同于两行的情况
选取四行时,floor(c/2)=2,假如上述都没有答案:
选取四行中1最少的一行来分析:
1.这一行为10……0,因为上述<4行时都不成立,所以2行时任意两行都不能相&为0,所以其他行第一列必须是1,第一列相加为4,不满足要求
2.这一行为11……0,第二行为10……,第三行为01……为了让第二行与第三行相&不为0,必须在后边的一列都为1,第四行为了与第二行和第三行相&不为0,必须拿出一列使得其与第一行、第二行和第三行都为1,这样算起来就需要6列数据,而题目所说列数小于等于5,所以不满足条件
选取超过四行时,类似方法证明答案不存在

func goodSubsetofBinaryMatrix(grid [][]int) []int {
	n := len(grid)
	if n == 0 {
		return nil
	}
	num := make(map[int]int, n)
	for i, row := range grid {
		mask := 0
		for j, v := range row {
			mask |= v << j
		}
		num[mask] = i
	}
	if v, ok := num[0]; ok {
		return []int{v}
	}
	for i, v := range num {
		for j, v2 := range num {
			if i&j == 0 {
				if v < v2 {
					return []int{v, v2}
				}
				return []int{v2, v}
			}
		}
	}
	return nil
}

最长合法子字符串的长度

func longestValidSubstring(word string, forbidden []string) int {
	n := len(word)
	if n == 0 {
		return -1
	}
	vis := make(map[string]struct{}, len(forbidden))
	for _, v := range forbidden {
		vis[v] = struct{}{}
	}
	l := 0
	ans := 0
	for i := 0; i < n; i++ {
		for j := i; j >= l && j >= i-10; j-- {
			if _, ok := vis[word[j:i+1]]; ok {
				l = j + 1
				break
			}
		}
		if i-l+1 > ans {
			ans = i - l + 1
		}
	}
	return ans
}

情侣牵手

var f []int

func fin(x int) int {
	if x != f[x] {
		f[x] = fin(f[x])
	}
	return f[x]
}

func union(x, y int) {
	fx, fy := fin(x), fin(y)
	if fx < fy {
		f[fy] = fx
	} else {
		f[fy] = fx
	}
}

func minSwapsCouples(row []int) int {
	n := len(row)
	f = make([]int, n/2)
	for i := 0; i < n/2; i++ {
		f[i] = i
	}
	for i := 0; i < n; i += 2 {
		union(row[i]/2, row[i+1]/2)
	}
	cnt := 0
	for i := 0; i < n/2; i++ {
		if f[i] == i {
			cnt++
		}
	}
	return n/2 - cnt
}

我们将 N 对情侣看做图中的 N 个节点;对于每对相邻的位置,如果是第 i 对与第 j 对坐在了一起,则在 i号节点与 j 号节点之间连接一条边,代表需要交换这两对情侣的位置。如果图中形成了一个大小为 k 的环:i→j→k→…→l→i 则我们沿着环的方向,先交换i,j 的位置,再交换 j,k 的位置,以此类推。在进行了 k−1 次交换后,这 k 对情侣就都能够彼此牵手了。

故我们只需要利用并查集求出图中的每个连通分量;对于每个连通分量而言,其大小减 111 就是需要交换的次数。

三个无重叠子数组的最大和

func maxSumOfThreeSubarrays(nums []int, k int) []int {
	sum1, sum2, sum3 := 0, 0, 0
	maxSum1, maxSum2, maxSum3 := 0, 0, 0
	m1_id := 0
	m2_id1, m2_id2 := 0, 0
	m3_id1, m3_id2, m3_id3 := 0, 0, 0
	for i := 2 * k; i < len(nums); i++ {
		sum1 += nums[i-2*k]
		sum2 += nums[i-k]
		sum3 += nums[i]
		if i >= 3*k-1 {
			if sum1 > maxSum1 {
				maxSum1 = sum1
				m1_id = i - 3*k + 1
			}
			if sum2+maxSum1 > maxSum2 {
				maxSum2 = sum2 + maxSum1
				m2_id1, m2_id2 = m1_id, i-2*k+1
			}
			if sum3+maxSum2 > maxSum3 {
				maxSum3 = sum3 + maxSum2
				m3_id1, m3_id2, m3_id3 = m2_id1, m2_id2, i-k+1
			}
			sum1 -= nums[i-3*k+1]
			sum2 -= nums[i-2*k+1]
			sum3 -= nums[i-k+1]
		}
	}
	return []int{m3_id1, m3_id2, m3_id3}
}

时间复杂度:O(n)
空间复杂度:O(n)

思想:滑动数组+贪心

数字1的个数

func digitOneInNumber(num int) int {
    ans:=0
    bitCount:=1
    low,cur,heigh:=0,num%10,num/10
    for cur>0||heigh>0{
        if cur==0{
            ans+=heigh*bitCount
        }else if cur==1{
            ans+=heigh*bitCount+low+1
        }else{
            ans+=(heigh+1)*bitCount
        }
        low+=cur*bitCount
        bitCount*=10
        cur=heigh%10
        heigh=heigh/10
    }
    return ans
}

将数字按照位分为前半段、当前位和后半段,然后分别讨论

滑动窗口的最大值

func maxInWindows( num []int ,  size int ) []int {
    // write code here
    if size==0{
        return nil
    }
    stack:=[]int{}
    ans:=[]int{}
    for i:=0;i<len(num);i++{
        for len(stack)>0&&num[stack[len(stack)-1]]<num[i]{
            stack=stack[:len(stack)-1]
        }
        stack = append(stack, i)
        for len(stack)>=0&&stack[0]+size-1<i{
            stack=stack[1:]
        }
        if i>=size-1{
        ans=append(ans, num[stack[0]])
        }
    }
    return ans
}

维护双端单调递减的队列。当前的num比队列后面的值大时,后面的值就没有存在的价值,因为现在的值比它大,也比它晚在滑动窗口中淘汰。依次从后往前淘汰掉值,然后再从头往前淘汰掉应该从滑动窗口中删除的值。最后头部的一定是最大值。如果当前的num比队列中的小,则直接放入到队列中,因为虽然它小,但是它新,可能会比大的晚淘汰。

简易计算器

func calculate(s string) int {
    stack:=[]int{1}
    ans:=0
    opt:=1
    var cur int
    for cur<len(s){
        switch s[cur]{
            case ' ':
                cur++
            case '+':
                opt=stack[len(stack)-1]
                cur++
            case '-':
                opt=-stack[len(stack)-1]
                cur++
            case '(':
                stack=append(stack,opt)
                cur++
            case ')':
                stack=stack[:len(stack)-1]
                cur++
            default:
                tmp:=0
                for cur<len(s)&&s[cur]>='0'&&s[cur]<='9'{
                    tmp=tmp*10+int(s[cur]-'0')
                    cur++
                }
                ans+=opt*tmp
        }
    }
    return ans
}

时间复杂度:O(n)
空间复杂度:O(n)

记录数字前面的符号,+就是1,-就是-1,然后数字乘以前面的符号就是它所贡献的值。
对于遇到(),我们把(之前的那个符号压栈,括号里面的符号要根据栈中最后一个符号来决定,-就是取当前符号数的相反数,+就是取当前符号数。

单词搜索

type Tire struct{
    end string
    next [26]*Tire
}

func (t *Tire)Insert(s string){
    cur:=t
    for _,v:=range s{
        if cur.next[v-'a']==nil{
            cur.next[v-'a']=&Tire{}
        }
        cur=cur.next[v-'a']
    }
    cur.end=s
}

func findWords(board [][]byte, words []string) []string {
    m:=len(board)
    if m==0{
        return nil
    }
    n:=len(board[0])
    if n==0{
        return nil
    }
    t:=&Tire{}
    for _,v:=range words{
        t.Insert(v)
    }
    ans:=[]string{}
    pos:=[4][2]int{{-1,0},{0,-1},{0,1},{1,0}}
    vis:=make([][]bool,m)
    for i:=0;i<m;i++{
        vis[i]=make([]bool,n)
    }
    var dfs func(x,y int,cur *Tire)
    dfs=func(x,y int,cur *Tire){
        if cur==nil{
            return
        }
        if len(cur.end)!=0{
            ans=append(ans,cur.end)
            cur.end=""
        }
        vis[x][y]=true
        for i:=0;i<4;i++{
            xx,yy:=x+pos[i][0],y+pos[i][1]
            if xx>=m||yy>=n||xx<0||yy<0{
                continue
            }
            if vis[xx][yy]{
                continue
            }
            dfs(xx,yy,cur.next[board[xx][yy]-'a'])
        }
        vis[x][y]=false
        return 
    }
    for i:=0;i<m;i++{
        for j:=0;j<n;j++{
            if t.next[board[i][j]-'a']!=nil{
             dfs(i,j,t.next[board[i][j]-'a'])
            }
        }
    }
    return ans
}

时间复杂度:O(m*n)
空间复杂度:O(26len(n))

对于要在矩阵中搜索的单词建立tire树,然后深度搜索矩阵中每一个位置开始,然后往四个方向走,如果走到的地方能够匹配上tire树,则tire树往下指

买卖股票的最佳时机IV

func min(x,y int)int{
    if x<y{
        return x
    }
    return y
}

func max(x,y int)int{
    if x>y{
        return x
    }
    return y
}

func maxProfit(k int, prices []int) int {
    if k==0{
        return 0
    }
    n:=len(prices)
    if n==0{
        return 0
    }
    dp:=make([]int,2*k)
    for i:=0;i<2*k;i++{
        if i%2==0{
            dp[i]=-prices[0]
        }
    }
    for i:=1;i<n;i++{
        for j:=0;j<2*k;j++{
            if j%2==0{
                if j==0{
                    dp[j]=max(dp[j],-prices[i])
                }else{
                    dp[j]=max(dp[j],dp[j-1]-prices[i])
                }
            }else{
                dp[j]=max(dp[j],dp[j-1]+prices[i])
            }
        }
    }
    return dp[2*k-1]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode是一个著名的在线编程库,可以帮助计算机程序员提升编程能力。LeetCode目涵盖了算法、数据结构、字符串、数组、链表等多个方面的知识,对于求职面试以及算法竞赛准备非常有帮助。 LeetCode上的编程目分为简单、等和困难三个难度级别。每个目都有详细的目描述、输入输出示例以及参考答案等内容。在解决每个问时,我们需要仔细阅读目,并根据目要求提供正确的解答。 通常,我们需要使用编程语言如Python、C++等来实现解思路。在编码之前,我们需要先分析问,并找到合适的算法或数据结构来解决问。一般来说,我们可以使用递归、迭代、动态规划等方法来解决常见的编程问。 在LeetCode上,我们可以提交解答,并测试解答是否通过了所有的测试用例。如果通过了所有的测试用例,我们就可以得到目的AC(Accepted)结果,并获得该目的通过证书。如果没有通过所有的测试用例,我们可以检查自己的解答,查找解答的错误或者不完善之处,并进行修改和优化。 总之,LeetCode编程是一个优秀的学习和练习编程的平台。通过解答LeetCode上的编程目,我们可以提升自己的编程能力,并且培养解决问的思维方式。无论是求职面试还是算法竞赛,LeetCode编程都是非常有帮助的资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值