[NOIP模拟]Fancy Signal Translate

这是一篇关于NOIP模拟题目的解析,主要讨论了一种特殊的01串加密方式。题目要求找到未出现过的最短串的长度。通过分析2的幂次与01串长度的关系,采用二分查找的方法来确定最短未出现串的长度。在实现过程中,由于细节处理不当导致原本简单的算法变得复杂,从而引发了性能问题。作者提醒做题时要仔细计算复杂度并避免不必要的错误。

题目描述:
FST是一名可怜的 OIer,他很强,但是经常 fst,所以 rating 一直低迷。
但是重点在于,他真的很强!他发明了一种奇特的加密方式,这种加密方式只有OIer才能破解。
这种加密方式是这样的:对于一个 01 串,他会构造另一个 01 串,使得原串是在新串中没有出现过的最短的串。
现在 FST 已经加密好了一个串,但是他的加密方式有些 BUG ,导致没出现过的最短的串不止一个,他感觉非常懊恼,所以他希望计算出没出现过的最短的串的长度。
输入格式:
一行,一个 01 串。
输出格式:
一行,一个正整数,表示没有出现过的最短串的长度。
样例输入:
100010110011101
样例输出:
4
数据范围:
测试点 1、2、3 的串长度≤10;
测试点 3、4、5 的串长度≤100;
测试点 6、7、8、9、10 的串长度≤105
题目分析:
考虑一个长度为i的串,显然它的排列组合共有2i种。对于给定的01串,如果一个长度为i的串在其中出现了不同的2i次,那么长度i的串就不存在没有过出现的情况,反之则有。因为给定01串长度最大为105,所以我们寻找的子串长度不超过17(217>105)。二分这个长度x(当然直接枚举也行,我二分的是1到20)。检查时,从原01串里依次取出长度为x的串,将其转为十进制,然后用check1数组标记,统计有多少个不同串。如果数目等于2x,则x还需增大,反之x减小。
总结:本来这是一道简单题,结果因为一个小错误导致只得了60分,跟一个暴力一样······我错在每次取出串放在一个数组里去转进制,数组我会清0,外面是有一个for循环,这个循环最大是O(n=105),本来数组大小是20就行,清0复杂度最大也就10520/32(memset的复杂度是O(n/32)),但我随手把数组大小赋成了105(因为其他数组的大小都是这个),然后就炸了,硬生生变成了105105/32。所以做题的时候要细心,要认真算一下复杂度,以前我做题算复杂度都忽略了清0操作,吃一堑长一智······
附代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<queue>
#include<set>
#include<map>
#include<cctype>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxn=1e5+10;
long long num,len,tot,a[maxn],s[20],d,b,ans;
bool check1[maxn];
char ss[maxn];

int ksm(int y)
{
    int ret=1;
    for(int x=2;y;x*=x,y/=2)
        if(y&1) ret*=x;
    return ret;
}

int zjz()//二进制转十进制
{
    int mi=1,sum=0;
    sum=s[len-1];
    for(int i=len-2;i>=0;i--)
        {
            mi=mi*2;
            sum=sum+s[i]*mi;
        }
     return sum;
}

bool check(int x)
{
    num=0;
    d=ksm(x);
    if(d>tot) return false;
    memset(check1,false,sizeof(check1));
    for(int i=1;i<=tot-x+1;i++)
    {
        len=0;
        memset(s,0,sizeof(s));
        for(int j=i;j<=i+x-1;j++)
            s[len++]=a[j];      
        b=zjz();
        if(check1[b]==false) 
        {
            num++;
            check1[b]=true;
        }
    }       
    if(num<d) return false;
    if(num==d) return true;
}

int half()
{
    int l=1,r=20,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid)==true)
            l=mid+1;
        else
            r=mid-1;
    }
    return l;
}

int main()
{
    //freopen("fst.in","r",stdin);
    //freopen("fst.out","w",stdout);

    scanf("%s",ss+1);
    tot=strlen(ss+1);
    for(int i=1;i<=tot;i++)
        a[i]=ss[i]-'0'; 
    ans=half();
    printf("%I64d",ans);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值