每日一题之字符串翻转到单调递增

前言

记录Leecode的解题过程是有益的,不记录思考中碰到的问题的话,就会减慢解题速度甚至将之前做的题忘记。模板确实是一个好的办法,不会模板不行,只会模板也不行。只看标签写问题直接套模板自然也不行。因此我将我做题的思路写出来,包括正确的和错误的,以供大家参考与指正。一般是分析每日一题。

926. 将字符串翻转到单调递增

如果一个二进制字符串,是以一些 0(可能没有 0)后面跟着一些 1(也可能没有 1)的形式组成的,那么该字符串是 单调递增 的。

给你一个二进制字符串 s,你可以将任何 0 翻转为 1 或者将 1 翻转为 0 。

返回使 s 单调递增的最小翻转次数。


思路分析

本题的难点在于最小,怎么确认是最小的反转次数,需要保证前面是0后面是1
思路 既然有一半为0一般为1,从前往后到全为0的翻转数且连续,再计算从后往前到全为1到翻转数,计算同一下标加起来最小的值。

    int minFlipsMonoIncr(string s) {
        int n=s.size();
        vector<int> dp0(n,0),dp1(n,0);
        dp0[0]=s[0]=='1'?1:0;//定义初值若第一位为1则翻转数初始为1
        dp1[n-1]=s[n-1]=='0'?1:0;//定义初值最后一位为0则翻转数为0
        for(int i=1;i<n;i++){
            dp0[i]=s[i]=='1'?dp0[i-1]+1:dp0[i-1];
            dp1[n-1-i]=s[n-i-1]=='0'?dp1[n-i]+1:dp1[n-i];
        }
        int ans=INT_MAX;
        for(int i=0;i<n;i++){
            ans=min(ans,dp0[i]+dp1[i]);
        }
        return ans-1;//由于重复计算了一次分割点
    }

但是时间空间复杂度都O(N),打败了30左右的用户明显不太行


官方思路:

官方思路的上述两个dp都是从前往后计算的,dp0与直观分析一样但dp1却略有不同

public:
	int minFilpsMonoIncr(string &s){
		int dp0=0,dp1=0;
		for(char c:s){
			int dp0New =dp0;//dp0沿用上面的方案
			dp1New = min(dp0,dp1);//dp1通过两个值来判断是否为分段点
			if(c=='1'){
				dp0New++;
			}else{
				dp1New++;
			}
			dp0=dp0New;
			dp1=dp1New;
		}
		return min(dp0,dp1);//返回最小值
	}

这一题的难点在于dp1New=min(dp0,dp1)。官方的思路是将上面的数组转换成了第i个字符是0的变换次数。其实也就说明了如果前面都是0的化则为000 (1)11的分界点(即dp0+1)是可行的。若前面已经有了分界点,则变为之前的0001(1)1继续延续下去。
这种思路可能没有这么直观,但是为了简化多往同一个方向上完成动态规划去考虑。


总结

本题意指找出最小的值,若真要进行搜索,本题数据范围较大,且最终结果不够清晰,需要找到最小翻转形态,与最小翻转次数,因此极为麻烦。可以跳过这种想法。因此本题应偏向于动态规划,原本的思路最终的答案可能与每一个之前的状态有关,不能进行进一步优化,至于动态规划滚动数组来减小空间时间复杂度是需要掌握的。
至于动态规划的题目最重要的是思考数组单个数据所蕴含的意义,本题可以由dp0的意义推测dp1的意义,再根据意义来推断dp1的求取的方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值