2 条题解

  • 0
    @ 2025-12-5 9:33:30

    VOID_DC 的题解中提到了这句话:

    容易发现 010 和 00010 的十进制值相同,而我们需要区分,可以直接对每个长度的子串独立开桶。

    但显然不用这么麻烦。

    我们把二进制换到三进制。

    这一位字符 这一位对应的值
    NULL 00(但我们不用,这一位并不会出现)
    0 11
    1 22

    这么赋值有什么好处呢?

    1. 区分了像 00101010 这样的子串。

    2. 按照子串的十进制值排序后得到的子串序列是按照字典序的。这样只用写双关键字排序,而非三关键字。

    3. 好编码,解码。

    4. 长度在一个区间的子串,其十进制值也在一个区间。

    • 0
      @ 2025-12-4 9:36:36

      所以说多行输入字符串,折行输出是什么鬼东西,被 corner case 崩飞了。

      题目传送门: vjudge链接

      思路

      切入点是 1AB121 \le A \le B \le 12,要求的字串太小了,考虑对每种长度的子串跑一个滑动窗口,复杂度 O((BA)n)O((B-A)n),记录所有合法子串即可,下面有几个细节。

      • 提取子串似乎可以 hash,但是没必要(其实是我不会),我们直接开桶记录子串二进制转十进制的值就可以了,通过一点位运算技巧可以在滑动的时候 O(1)O(1) 实现转移。
      • 容易发现 010 和 00010 的十进制值相同,而我们需要区分,可以直接对每个长度的子串独立开桶。
      • 子串长度可能大于字符串长度,这种非法情况要直接特判崩掉(导致我挂分的原因)。
      • 输出的时候要十进制还原二进制,还原需要十进制值和二进制位数(子串长度)。还需要按出现次数与字典序双关键字排这3个值。
      • 每6个折行输出是真 cs,注意不要多输出一些奇怪的换行。

      超级无敌巨帅的含注释代码

      #include<iostream>
      #include<cstdio>
      #include<cmath>
      #include<algorithm>
      using namespace std;
      int ans[15][10000];//ans[i][j]: sum(len=i & number_j)
      int mi[13]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095};//预处理二进制下全 1 的序列 
      int n,m,k;string t,s;
      struct node{
      	int val,len,cnt;//十进制值,二进制长度,出现次数 
      }q[300000];int cnt;
      int ch(char x){
      	if(x=='1') return 1;
      	return 0;
      }
      bool cmp(node a,node b){//三关键字排序 
      	if(a.cnt==b.cnt){
      		if(a.len==b.len) return a.val<b.val;
      		return a.len < b.len;
      	}
      	return a.cnt>b.cnt;
      }
      void re(int val,int len){//十进制还原二进制 
      	string res;
      	int rr=0;
      	for(int i=len-1;i>=0;i--){
      		if(val&(1<<i)) cout << 1;
      		else cout << 0;
      	}
      	return;
      }
      int main(){
      	cin >> n>> m >> k;
      	while(cin >> t){s+=t;}//处理多行输入的字符串 
      	int len=s.length();
      	s=" "+s;
      	for(int i=n;i<=m;i++){
      		if(i>len) break;//特判非法情况 
      		int tmp=0;//记录当前这个窗口的十进制值 
      		for(int j=1;j<=i;j++){//处理窗口的初始位置 
      			tmp<<=1;
      			tmp|=ch(s[j]);
      		}
      		ans[i][tmp]++;//开桶记录 
      		for(int r=i+1;r<=len;r++){
      			tmp<<=1;//先左移一位空位
      			tmp&=mi[i];//通过 $ 长为i的全1序列 去除不合法的最高位,其他位置不变 
      			tmp|=ch(s[r]);//加入下一位 
      			ans[i][tmp]++;//记录 
      		}
      	}
      	for(int i=n;i<=m;i++){
      		for(int j=0;j<=9000;j++){
      			if(ans[i][j]){
      				q[++cnt]=node{j,i,ans[i][j]};//导入struct方便排序 
      			}
      		}
      	}
      	sort(q+1,q+cnt+1,cmp);
      	int tot=0,outcnt=0;
      	for(int i=1;i<=cnt;i++){//处理输出 
      		if(q[i].cnt!=q[i-1].cnt&&tot==k) break;
      		if(q[i].cnt!=q[i-1].cnt){
      			tot++;outcnt=0;
      			if(i==1) cout << q[i].cnt << '\n';
      			else cout <<'\n'<< q[i].cnt << '\n';
      		}
      		re(q[i].val,q[i].len); cout << ' '; 
      		outcnt++;
      		if(outcnt==6){
      			outcnt=0;
      			if(q[i+1].cnt==q[i].cnt) cout << '\n';
      		}
      	}
      	return 0;
      } 
      
      • 1

      信息

      ID
      602
      时间
      1000ms
      内存
      256MiB
      难度
      7
      标签
      (无)
      递交数
      27
      已通过
      7
      上传者