18 一个01字符串,求出现0、1出现次数相等的最长子串长度
发布日期:2021-05-08 19:31:10 浏览次数:14 分类:精选文章

本文共 2767 字,大约阅读时间需要 9 分钟。

      一个01字符串,求出现0、1出现次数相等的最长子串长度

题目描述:

    已知一个长度为N的字符串,只由0和1组成, 求一个最长的子串,要求该子串出0和1出现的次数相等。

    要求算法时间复杂度尽可能的低。

    比如:  10000010111000001,所求为8,加粗的部分有4个0、4个1  

 

最简单的想法就是遍历所有的子串,之后判断该子串是否满足条件N^2子串,每个子串扫一遍判断0、1是否出现的次数相等,复杂度为O(N^3)

 

方法一:
稍加思考就会发现, 如果一个长度为n的子串满足条件,加么这n个元素的和加起来一定=(n/2),且
子串长度为偶数,这样在循环的过程中,增量加就可以了,不需要每个子串从头计算,复杂度降为O(N^2).
该思想的程序为fun():

 

 

int fun(char num[],int n){    int i,j;    int maxlen=0;//所求    int sum,currentlen;//当前子串和与当前子串长度   //挨个判断每个子串是否符合要求,若符合,则更新maxlen   for(i=0;i
maxlen) maxlen = currentlen; } } return maxlen;}

方法二:

和fun()的主体差不多,设了一个sum来判断每个子串中0,1个数是否相等,遇到0,sum--;遇到1,sum++。fun()和fun2()求子串的思路和方法要领会,它是以求以每个字符开头的所有子串这样的方式求的,外面再包个大循环就能求出所有子串。

int fun2(char num[],int n){    int i,j;    int maxlen=0;//所求    int sum,currentlen;//当前子串和与当前子串长度   //挨个判断每个子串是否符合要求,若符合,则更新maxlen   for(i=0;i
maxlen) maxlen = currentlen; } } return maxlen;}
方法三:
一种巧妙的解法:定义一个数据temp[N], temp[i]表示从num[0...i]中 num_of_0 - num_of_1,0的个数与1的个数的差那么如果num[i+1] ~ num[j]是符合条件的子串,一定有 temp[i] == temp[j],因为中间的部分0、1个数相等,相减等于0。只需要扫一遍num[N]就能把temp[N]构造出来了。这样问题就转换成了求距离最远的一对数,使得temp[i] == temp[j],因为temp[i]的范围一定是[-N,N],-N到N的范围所对应的从num[0]开始的子串的结尾下标i都存起来,这样每扫到temp[i],查数就行了。
 
 
设两个辅助数组temp[]与assist[],代表的意义如下:
temp[i]表示num[0...i]中0的个数与1的个数差(即0的个数减去1的个数),则必有 -n<=temp[i]<=n
 
assist[]为辅助数组,里面存了0,1个数差所对应的num的最小下标,初值为-1表示该处还未存入内容。
assist[0]表示0,1个数差为-n的从num[0]开始的子串的结尾位置,即num[0...i]中的i,此时num[0...i]串中0,1个数差为-n,assist[2*n]表示0,1个数差为n的从num[0]开始的子串的结尾位置,即num[0...i]中的i,此时num[0...i]串中0,1个数差为n,assist[k]表示0,1个数差为 k-n 的从num[0]开始的子串的结尾位置。即assist[k+n] = i 表示0,1个数差为k的子串为num[0...i]
 
int fun1(char num[],int n){    int maxlen=0;//所求    int currentlen;//当前子串长度    int temp[n];    int assist[2*n+1];  //temp[]和assist[]的意义见上    int i;    int count[2]={0,0};//分别代表当前0和1的个数,count[0]为0的个数,count[1]为1的个数   for(i=0;i<2*n+1;i++)       assist[i] = -1;          for(i=0;i
maxlen) maxlen = currentlen; } } return maxlen;}int main(){ char num[]="10000010111000001"; int maxlen = fun(num,strlen(num)); printf("maxlen=%d\n",maxlen); return 0;}

 

最后一种解法

/** *  * 数字串,全为0,1组合而成。求一个最长的连续子串,其中0和1的个数相等。 * 比如: 01100111 --> 011001 * 110000011 --> 1100 / 0011 *  * 方法: * 定义一个数组a,a[0] = 0 * 然后遍历输入输入数字串,遇到0,减1,遇到1,加1。含义就是,到目前位置,净值出现了多少个0或者1. * 在数组a中选择两个相等的值,比如都是-3,第一个-3表示到该元素为止,多出现了3个0,第二个-3还是表示 * 到该元素为止,多出3个0,那么这两个元素之间的0和1的个数必定相互抵消。 * 于是,所求子串等同于,数组a中距离最远的两个相同值。可以两次循环,复杂度o(n*n) *  * 为了O(n)的复杂度,利用hash表代替数组。原理一样。 *  **/public class ZeroOnePairs { 	public static void find(int[] input){		HashMap
map = new HashMap
(); map.put(0, 0); int begin = 0; int max = 0; int current = 0; for(int i=0; i
max){ max = i + 1 - l; begin = l; } } } for(int j=begin; j

 

参考文献

 

在这里插入图片描述

上一篇:19 均分钱币(0 1背包问题)
下一篇:关系型数据库和非关系型数据及其区别

发表评论

最新留言

不错!
[***.144.177.141]2025年03月24日 09时10分36秒