模拟赛之AB串

来源:互联网 发布:android 无数据提示 编辑:程序博客网 时间:2024/05/03 05:16

AB

   题意:给出一个只含有AB的字符串S,求解一个串T使得T的长度最短且T中不含有S中的任意一个连续子串.(S的长度<=200000)
    内存限制 :1M
分析(字比较多但是思想还是比较明了的)
    首先考虑暴力
    枚举T的长度后搜索获得T后在S中查找是否含有T,找到长度最短的T输出.
    这样只开一个数组和一个string(写深搜)不会MLE
    这个暴力如果不写残可以过掉40分←_←
    
    然后分析性质
    由于这个S只有A和B两种字符,那么用01来压位是比较容易想到的
    可以用一个整型变量记录S中连续的子串,然后记录下来其出现过
    最后枚举下所有子串判断是否出现过
    还有很重要的性质就是其答案长度必然不大于logN,这是枚举子串可行的条件之一,有了这个性质枚举的长度就保证了不会超时(m=logN,2^m >= n 所以m长度的子串中必然有答案)

    如果没有空间限制的话这样枚举长度后接着枚举S中该长度的子串标记下存在(vis),然后枚举该长度的所有子串判断是否出现过即可。
    但是该题内存限制1M,开这么多数组很容易超(最后一个数据其实是两百万的长度…)
    
    所以考虑用二进制数来表示状态,可以把S每32位放到一个整型数组单元里记录,记录01的情况也就是AB,这样只需要N/32的空间,取出的时候就判断一下是在哪一段(第几个32)然后>>相应位数(i%32)再&1把第一位取出即可
    取出指定位数的子串后要标记下该子串出现过,子串的长度是比较短的可以直接用bool数组标记。
    最后枚举该长度的所有子串找到第一个没有出现过的输出然后return 0;
    
    总结下我们开了一个(200000/32)的int数组和一个(200000)bool数组,完全可以过掉这个题了,但我们还可以进行一些空间优化
    
    这个每个子串是互不相同的,所以可以将子串是否出现过作为一种状态压位
    将每32个子串的是否出现过的状态记到一个int

    这个代码是用的这个实现的,代码很短但这个01压位的思想值得一看
    下面就放代码了

代码

#include <cstdio>#include <cstring>#include <iostream>using namespace std;const int Maxn = 32801;unsigned int s[Maxn*2],v[Maxn];unsigned int p,z,i,l,n,d;char c;int main(){//freopen("ab.in","r",stdin);//freopen("ab.out","w",stdout);for(c=getchar();c>32;c=getchar(),++n){if(c=='B')s[n/32] += 1<<(n%32);}for(l=1;;l++){z = (1<<l)-1;//用于取出长度为l的子串for(p = 0;p <= n / 64;p++)v[p]=0;for(p=0,i=0;i<n;i++) {//从0到n-1记录的字符串的下标p=((p<<1) + (s[i/32]>>(i%32) & 1)) & z;//枚举连续的长度为l的子串//当然前提是取够l位,一开始是先一位一位取,后舍弃最前端在后面补if(i+1 >= l)v[p/32] |= 1<<(p%32);//把当前子串的10状态(是否存在)压到V数组里}for(p=0;p<=z;p++){//枚举子串if(!(v[p / 32]>>(p % 32) & 1)){//在V数组中取出当前枚举串的状态for(i=l;i;i--)putchar('A' + ((p)>>(i-1)&1));//取出当前串的字符输出答案return 0;}}}return 0;}

End。
    
    
    
    
    
    
0 0
原创粉丝点击