问题重现:
思路介绍:
注意一点这个子数组是原数组的一个连续的子集(在我们分析样例输出时,我们会发现,如果不要求连续的话,会有许多的可能,同时通过进一步测试,会发现我们的想法是对的),同时这里的字母指的是大小写都可,数字不包括负数,只承认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;
}
}