序
昨晚有两道题没有做出来。其中一题是01字符串的输入,求经过转换后连续1的子串的最大长度。结束之后和同学讨论,得出了一个非常满意的结果。
问题重述
输入是一个01
字符串s
,其中可以做k次转换操作(将0
变成1
),求经过k
次转换之后能够得到的最长的连续1
子串的长度。
示例1:
输入:101010101 3
输出:7
注:可以对字符串做3次转换操作,可有111111101或111110111或111011111或101111111四种操作,最长的连续1子串为7个1.
示例2:
输入:11010111001010 2
输出:8
注:将前两个0变成1,则得到11111111001010,有8个1连续,其余的操作都得不到更多的,所以输出为8。
解决方法
最开始看到这道题,基本没啥思路,就想着要不要暴力求解,把所有的情况列出来然后再向动态规划和贪心方向优化,以及是否可以二分递归地做下去。但是后面变量的值可能很大,就会导致整个搜索空间很复杂,展开过程也比较繁琐。然后和另一名同学进行了友好地辩论,在辩论中发现了一个规律,正是这个规律引导了我们巧妙的解决了这个问题。
因为这个问题是求最大值的问题,那我们发现,如果只能够更改两个0,被更改的0一定是相邻的(此处的相邻指:两个0之间可能有1,但一定不能有0),不然其中一个0的改变就失去了意义。同理可以想到,第k个0和前面k-1个0也一定是相邻的,不然这个更改就没有作用。
由此我们可以想到一个好的思路,只需要依次搜索k
个相邻的0
所构成的解,找到最大值即可,通过第一个0
和最后一个0
向两边拓展,找到当前的最长子串,然后得出全局的最长子串即可。
这个思路已经将复杂度降至很低了,不过还是需要每次向两个方向拓展,找到所有的1
,这个过程是否可以省略呢?
答案是可以!我们可以通过遍历字符串得到0
的位置并存入数组中,其中k
个0
(eg,从i
位置到j
位置,正好为k
个相邻的0
)外面的1
字符长度可以直接通过i-1
和j+1
得到,因为这两个位置是最近的0
位置。
由此,我们的代码按照上面的思路写了出来:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
string input;
cin>>input;
int windows=0;
cin>>windows;
int length=input.size();
vector<int> logs;
for(int i=0;i<length;i++)
{
if(input[i]=='0')
logs.push_back(i);
}
//first view
int longestNum=logs[windows];
int logsSize=logs.size();
//full view
for(int i=1;i<logsSize-windows;i++)
{
int temp=logs[i+windows]-logs[i-1]-1;
if(longestNum<temp)
longestNum=temp;
}
//final view
if(longestNum < (length-logs[logsSize-windows-1]-1) )
longestNum=(length-logs[logsSize-windows-1]-1);
cout<<longestNum;
return 0;
}