模拟赛之AB串

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。
    
    
    
    
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值