LeetCode 6157. 二进制字符串重新安排顺序需要的时间 DP题解。
题目
6157. 二进制字符串重新安排顺序需要的时间
https://leetcode.cn/problems/time-needed-to-rearrange-a-binary-string/
给你一个二进制字符串 s 。在一秒之中,所有 子字符串 “01” 同时 被替换成 “10” 。这个过程持续进行到没有 “01” 存在。
请你返回完成这个过程所需要的秒数。
这道题是我第一次参加LC周赛的第二题。看着简单,实际上有点东西。
暴力replace
思路
n^2
的思路:每一秒我们都把01
子串替换为10
,直到字符串中没有01
。
这个做法在比赛当时是可以通过的,第二天LC官方应该是补上了这个漏洞,这个做法复杂度也高,不是题目想考的做法。
代码
|
|
复杂度分析
时间
每一轮中都需要进行字符串遍历,整体处理O(n^2)
时间。
空间
无额外空间占用,空间复杂度O(1)
。
DP
思路
解法1时间效率不满足要求!
我们来分析下用例,看看能不能产生新的解法:
题干中给出了示例10110101
,每秒替换之后的结果:
|
|
我们记每个1为:arr[i]
。
阶段 | 其左侧0的个数 | 挪动到对应最左所需秒数 | 状态 |
---|---|---|---|
第一个1,arr[1] |
1 | 1 | 第一个1替换一次,到达其最终终点 |
第二个1,arr[2] |
1 | 2 | 由于第一秒第二个1前面紧挨了第一个1,第一秒没动,第二秒才到达终点 |
第三个1,arr[3] |
2 | 3 | 第一秒向左挪动了一位,第二秒与arr[2] 紧挨,等待一秒,第三秒到达终点 |
第四个1,arr[4] |
3 | 4 | 第一秒向左挪动了一位,第二秒向左挪动一位,第三秒与arr[3] 紧挨,等待一秒,第四秒到达终点 |
我们结合另一个用例001011
,每秒替换之后的结果:
|
|
阶段 | 挪动到对应最左所需秒数 | 其左侧0的个数 | 状态 |
---|---|---|---|
第一个1,arr[1] |
2 | 2 | 第一二秒均为01排列,第二秒挪动到最左 |
第二个1,arr[2] |
3 | 3 | 第一二三秒均为01排列,第三秒挪动到最左 |
第三个1,arr[3] |
4 | 3 | 第一秒等待,第二三四秒均01排列,第四秒到达终点 |
因此,我们可归纳如下要素:
要素 | |
---|---|
状态表示 | dp[i] 表示将 s[i] 处的1挪动到最左侧最少需要的秒数;pre0 表示当前位置前序0个数; |
阶段划分 | 每个1出现均为新的阶段,0维持与前序相同的状态值; |
转移方程 | s[i]=='0'时,dp[i]=dp[i-1] ;s[i]=='1' && pre0!=0时,dp[i]= max(dp[i-1]+1,pre0) ,dp[i-1]+1 表示1前序为1,需要多一次移动; |
边界 | dp[0]=0; |
目标 | max(dp[i]) |
特别注意:
dp[i-1]+1
表示,如10101
/10110
典型用例中,后序1挪动到位都需付出比前序1更多1的时间;10101
第三个1比第二个1,多消耗1个时间,原因是pre0
增加了1;10110
第三个1比第二个1,多消耗1个时间,原因是第三个1紧挨着第二个1,第三个1需先等待1秒再左移;- 综上,
dp[i]= max(dp[i-1]+1,pre0)
;
pre0==0
即前序还没0时,1此时状态不需要挪动,即dp[i]=0
;
代码
|
|
滚动数组压缩状态:
|
|
复杂度分析
时间
整个过程仅需遍历线性次数组,复杂度 O(n)
。
空间
额外维护了一个dp
数组,空间复杂度O(n)
。
此处可以进行滚动数组压缩状态,维护一个prevDp
,对应复杂度O(1)
。