力扣每日一题:统计1的显著的字符串数目
显著字符串:字符串中,0的个数的平法小于等于1的个数
思路:
首先,考虑暴力做法,即从左到右依次遍历每个子字符串,外层从下标为0开始,设置cnt0和cnt1分别记录0和1的个数,内层从0开始,不断向以外层下标为首元素的字符串中加入新元素,每次加入元素都更新cnt0和cnt1,并判读当前是否是显著字符串。该方法在测试阶段最后会超时。
其次,考虑是否可以使用滑动窗口,由于向右移动,无法确定下一个包括进来的字符是0还是1,因此无法使用滑动窗口动态增减0和1的个数。
再然后,考虑固定当前位置,以当前位置为最后一个元素,不断包含前面的元素,并判断是否是显著字符串。因为固定了当前位置,即求出的所有满足的显著字符串都是以当前位置结尾,因此一定不会重复。优化的地方,就在于如何在固定当前位置的前提下,快速得到显著字符串的个数。
优化方式是记录当前位置之前的0的下标,当固定了0的下标后,就可以通过每次新包含一个0,然后判断包含这些0以后,显著字符串的个数,继续保持不会重复。
对于一次判断过程中,假设当前包含的最左的0的下标是q,再左侧的0的下标是p,当前一定要包含的元素的下标是外层的r,则判断[q,r]的子串加入[p,q]且以p结尾的子串后符合的个数,此时因为[p,q]之间只有1,没有0,因此可以快速判断,从加入几个1开始,一定都符合显著字符串的要求。
即:当前,从[q,r],有cnt1个1,cnt0个0,(p,q]之间,有q-p个元素,在额外去掉cnt0*cnt0-cnt1个不符合条件的位置后(因为需要新增cnt0*cnt0-cnt1个1),就得到符合条件的个数了。
class Solution {
public:int numberOfSubstrings(string s) {vector<int>pos0={-1};int total1=0;int ans=0;for(int r=0;r<s.size();r++) //计算包含r位置,从[0,r]的符合条件的子串{if(s[r]=='0'){pos0.push_back(r);}else{total1++;ans+=r-pos0.back();//单独计算出,不含任何0的符合条件的子串}int m=pos0.size();for(int i=m-1;i>0&&(m-i)*(m-i)<=total1;i--){int p=pos0[i-1],q=pos0[i];int cnt0=m-i;int cnt1=r-cnt0-q+1; //从[q,r]的1的个数ans+=max(q-max(cnt0*cnt0-cnt1,0)-p,0);//计算包含q位置的0,不包含p位置的0符合条件子串}}return ans;}
};