当前位置: 代码迷 >> 综合 >> CF2017.10.5 D. Huge Strings
  详细解决方案

CF2017.10.5 D. Huge Strings

热度:91   发布时间:2023-10-29 10:55:52.0

题目大意

开始你有n个01串,长度不超过100
然后有m个操作,次数不超过100
每一个操作两个数x,y
就是说吧y拼接到x后面形成一个新的字符串
要你找出一个最大的k,长度为k的所有01串都是这个新的字符串的子串

题解

这题又是一个古怪的结论题?
首先,想这种类型的题答案肯定都很小。。
我们考虑到,一开始的字符串,k最大就差不多是7的样子·
然后你如果将两个串合并a+b
那么答案也不会增加太多
于是膜了题解题解取的是13。。
大概就这么多吧。。
然后呢,我们知道,对于合并的两个串,只有头13个和尾13个是有用的
因为你要是超出的话,他们并起来并不可以得出一个有用的答案。。
于是对于合并的串,我们不用把它完全合并起来,只需要保存头13个和尾13个就可以了
那么我们怎么判断一个子串是否有出现过呢?
我们可以hash一下,然后对于每一个长度都开一个bitset来维护一下,表示这个串在这个长度下那些状态出现过。。
如果长度为k的串,bitset里面的状态为2k的话,那么就说明所有子串都在了
然后没有了
CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<bitset>
#include<cstring>
using namespace std;
const int MAX=13;
char ss[220];
struct node
{char L[50],R[50];int len;bitset<8200>b[MAX+1];void read(){scanf("%s",ss+1);len=strlen(ss+1);for (int u=1;u<=MAX;u++) b[u].reset();for (int u=1;u<=len;u++){int s=0;//hash for (int i=1;i<MAX&&(u+i-1<=len);i++){s=s*2+(ss[u+i-1]-'0');b[i].set(s);}}for (int u=1;u<=MAX;u++) L[u]=ss[u];for (int u=1;u<=MAX;u++) R[u]=ss[len-u+1];len=min(len,MAX);}friend node operator + (node x,node y){node z;z.len=min(x.len+y.len,MAX);for (int u=1;u<=MAX;u++) z.b[u]=x.b[u]|y.b[u];int l=0;for (int u=x.len;u>=1;u--) ss[++l]=x.R[u];for (int u=1;u<=y.len;u++) ss[++l]=y.L[u];for (int u=1;u<=l;u++){int s=0;for (int i=1;i<=MAX&&(u+i-1<=l);i++){s=s*2+(ss[u+i-1]-'0');z.b[i].set(s);}}for (int u=1;u<=x.len;u++) z.L[u]=x.L[u];for (int u=1;u<=y.len;u++) z.L[u+x.len]=y.L[u];for (int u=1;u<=y.len;u++) z.R[u]=y.R[u];for (int u=1;u<=x.len;u++) z.R[u+y.len]=x.R[u];return z;}int pre (){for (int u=MAX;u>=1;u--)if (b[u].count()>=(1<<u)) return u;}
}str[210];
int n,m;
int main()
{scanf("%d",&n);for (int u=1;u<=n;u++)str[u].read();scanf("%d",&m);for (int u=1;u<=m;u++){int x,y;scanf("%d%d",&x,&y);str[u+n]=str[x]+str[y];printf("%d\n",str[u+n].pre());}return 0;
}