面试题 17.05. 字母与数字

问题重现:

思路介绍:

        注意一点这个子数组是原数组的一个连续的子集(在我们分析样例输出时,我们会发现,如果不要求连续的话,会有许多的可能,同时通过进一步测试,会发现我们的想法是对的),同时这里的字母指的是大小写都可,数字不包括负数,只承认0-9的单个数字.

        发现它是求连续子集,我们很自然的相等使用前缀和. 我们把字母看成1 数字看成-1.

        前缀和数组为sum[1...n],sum[i]表示从下标0(nums[0])到下标i(nums[i])共(i+1)个元素的对应1or-1的前缀和------sum[i] += sum[i-1],寻找前缀和为0(sum[i]==0)或者两个前缀和相等的(sum[i]-sum[j]==0)

        值得说的是,我们应该使用hash散列来降低时间复杂度,不然对应上述的i,j的遍历为O(n^2)对应10^5的输入会超时(我尝试过,会把该代码粘在后面).使用hash后可降低到O(n).

        代码如下:

    vector<string> findLongestSubarray(vector<string>& array) {
        vector<string> ans;
        int len=array.size(); vector<int> sum;
        for(string str:array){
            if((str[0]>='a'&&str[0]<='z')||(str[0]>='A'&&str[0]<='Z')){
                sum.push_back(1);
            }else if(str[0]>='0'&&str[0]<='9'){
                sum.push_back(-1);
            }
        }

        int index_x=-1,index_y=-1,max;//这里index_x初始化为-1是一点小心机,为了和后面从index_x+1到index_y的push_back对应上
        for(int i=1;i<len;i++){
            sum[i] += sum[i-1];
        }
//对于max的初始化  ->   先看一看从0开始的有没有符合的
        for(int i=0;i<len;i++) if(sum[i]==0) {max=i+1;index_y=i;}
        //这里不用使用 "sum[i]==0&&(i+1)>max"  
        //因为如果更后面的i对应的sum[i]是0  自然满足(i+1)>max
        
//使用map降低时间复杂度
        map<int,int> map1;
        for(int i=0;i<len;i++){
            if(map1.count(sum[i])){
                //找到了要不要覆盖???
                //针对此题 不需要
                if((i-map1[sum[i]])>max){
                    max=i-map1[sum[i]];
                    index_x=map1[sum[i]];
                    index_y=i;
                }
            }else{
                map1[sum[i]]=i;
            }
        }
//输出操作  
        for(int i=index_x+1;i<=index_y;i++){
            ans.push_back(array[i]);
        }
        return ans;
        //亦可return vector<string>(array.begin()+index_x+1,array.begin()+index_y+1)
        //如果没有元素(index_y==-1)    return {};  c11特性 等价于无参构造
    }

注意事项:        

这里注意几点:

        1.对于可能的答案是有两种, 前缀和为0(sum[i]==0)或者两个前缀和相等的(sum[i]-sum[j]==0)我的代码是分开操作,当然也可以在图中加入一个map1[0]=-1,表示从负下标到0下标的前缀和sum[0]=-1,这样可以在图里统一操作

        2.对于map,怎么使用hash,我是从暴力(遍历i,j)到哈希,开始犯了一个错误,只想着逻辑简单,先遍历一遍,把所有i存进去map1[sum[i]]=i,再遍历一遍,查找所有和之前存入的有对应关系的下标,但是这有一个很严重的逻辑问题就是map1[sum[i]]=i会发生覆盖,这样就势必会发生错误.

        正确的做法是,在遍历中的每一步(这里记遍历的循环变量为k)进行查找,这里的k相当于双重循环里面的j,而map1[sum[i]]相当于双重循环里面的i,如果没有找到对应元素,说明当前键值对应该被存入,而如果找到了map中的对应元素,说明此时可以进行结果的比较,是否更新max,不过这里还有一个小问题应该注意到: 

        3.那就是我们得到了在当前的循环变量下,我们有了一个新的键值对,但是键已经存在,我们要不要覆盖掉原来的键值对,我的建议是根据实际情况具体分析,如果太复杂可以考虑使用multimap或者把值value设为vector,而对于本题,因为它让找最大的子数组,我们无需覆盖,考虑如下情况sum[a]=1,sum[b]=1,sum[c]=1,a<b<c. 如果我们用b覆盖了a,就只能看到c-b,无法发现更大的max(c-a)

        //双重循环的超时操作
        for(int i=0;i<len;i++)
            for(int j=i+1;j<len;j++)
                if(sum[j]-sum[i]==0){
                    if((j-i)>max){
                        max=j-i;
                        index_x=i;
                        index_y=j;
                    }
                }

        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值