题意

字符串A有N个子串B1,B2,…,Bn。如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)

这样A中的若干字符就被这N个子串覆盖了。问A中能被覆盖字符个数的最小值和最大值。

字符串长度\(A<=10000,N<=4\),子串长充\(<=1000\)

分析

参照_Gloid的题解。

首先kmp求出每个子串能放在哪些位置。接下来的两部分贪心和状压都可以,各取比较方便的。

最大值

考虑贪心。考虑枚举子串的左端点出现顺序,在此基础上每个子串的位置肯定都应该尽量靠前,有是否与上个子串有交两种选择,如果有交一定会使交集最小,于是枚举第一个子串出现位置并暴力枚举\(4!*2^3\)种情况。时间复杂度\(O(N! 2^{N-1})\),随便跑。

最小值

考虑状压。首先把被包含的子串去掉方便处理。将线段排序,设\(f[i][S]\)考虑前\(i\)条线段已出现的子串集合为\(S\)时的最小覆盖长度,转移时考虑上条线段是否与其有交,单调队列优化转移(因为懒写了线段树)。虽然非常麻烦但可能还是比贪心好点的。而最大值由于不能删掉被包含子串状压简直没法做。

关于线段树存的内容的含义,线段树处理线段相交的情况,\(dp[i]=dp[j]+i-j\)这种形式的DP,可以先把\(dp[j]-j\)存下来,然后读取的时候直接读该位置的最大/最小值\(+i\)即可。这里存的就是\(f[i][S]-r[i]\)。

时间复杂度\(O(A 2^N \log_2 A)\),是2126033.9807279119026370044348732。

代码

利用函数入栈顺序简化了代码。话说我之前都是被这个入栈顺序坑了……

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>il T read(rg T&x)
{
return x=read<T>();
}
typedef long long ll;
using namespace std; co int N=1e4+1;
char s[N],t[5][N];
int n,m,len[5],cnt[5];
int fail[N];
struct data
{
int l,r,id; bool operator<(co data&x)co
{
return l<x.l;
}
}b[5][N],c[4*N]; namespace MAX
{
bool flag[5];
int ans; void dfs(int num,int pl,int pr,int sum)
{
if(num==m)
{
ans=std::max(ans,sum);
return;
}
for(int i=1;i<=m;++i)if(!flag[i])
{
int u=0,v=0;
for(int j=1;j<=cnt[i];++j)if(b[i][j].l>=pl)
{
if(b[i][j].l<=pr) u=j;
else {v=j;break;}
}
flag[i]=1;
if(u)
dfs(num+1,b[i][u].l,std::max(pr,b[i][u].r),sum+std::max(b[i][u].r-pr,0));
if(v)
dfs(num+1,b[i][v].l,std::max(pr,b[i][v].r),sum+b[i][v].r-b[i][v].l+1);
flag[i]=0;
}
} int solve()
{
// cerr<<"solve max"<<endl;
ans=0;
dfs(0,0,0,0);
return ans;
}
} namespace MIN
{
int min[N*4][1<<4],f[N*4][1<<4],tree[N<<4][1<<4]; void rebuild()
{
bool flag[5]={0};
for(int i=1;i<=m;++i)
for(int j=1;j<=m;++j)
if(i!=j&&len[i]<len[j]||len[i]==len[j]&&i>j)
for(int k=1;k<=cnt[i];++k)
if(b[i][k].l>=b[j][1].l&&b[i][k].r<=b[j][1].r) {flag[i]=1;break;}
n=0;int m2=0;
for(int i=1;i<=m;++i)if(!flag[i])
{
for(int j=1;j<=cnt[i];++j)
c[++n]=b[i][j],c[n].id=m2;
++m2;
}
m=m2;
sort(c+1,c+n+1);
} void insert(int x,int s,int l,int r,int p,int v)
{
tree[x][s]=std::min(tree[x][s],v);
if(l==r)
return;
int m=(l+r)/2;
if(p<=m)
insert(x<<1,s,l,m,p,v);
else
insert(x<<1|1,s,m+1,r,p,v);
} int query(int x,int s,int l,int r,int ql,int qr)
{
if(ql>qr)
return N;
if(ql<=l&&r<=qr)
return tree[x][s];
int m=(l+r)/2;
if(qr<=m)
return query(x<<1,s,l,m,ql,qr);
if(ql>=m+1)
return query(x<<1|1,s,m+1,r,ql,qr);
return std::min(query(x<<1,s,l,m,ql,qr),query(x<<1|1,s,m+1,r,ql,qr));
} int solve()
{
// cerr<<"solve min"<<endl;
rebuild();
memset(f,0x3f,sizeof f);f[0][0]=0;
memset(min,0x3f,sizeof min);min[0][0]=0;
memset(tree,0x3f,sizeof tree);
int p=0;
for(int i=1;i<=n;++i)
{
while(c[p+1].r<c[i].l) ++p;
for(int j=0;j<(1<<m);++j)if(j&(1<<c[i].id))
f[i][j]=std::min(min[p][j^(1<<c[i].id)]+c[i].r-c[i].l+1,query(1,j^(1<<c[i].id),1,n,p+1,i-1)+c[i].r);
for(int j=0;j<(1<<m);++j)
{
min[i][j]=std::min(min[i-1][j],f[i][j]);
insert(1,j,1,n,i,f[i][j]-c[i].r);
}
}
return min[n][(1<<m)-1];
}
} int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int kase;
read(kase);
while(kase--)
{
scanf("%s",s+1);
n=strlen(s+1);
read(m);
for(int i=1;i<=m;++i)
{
scanf("%s",t[i]+1);
len[i]=strlen(t[i]+1),cnt[i]=0;
for(int j=1;j<len[i];++j)
{
int k=fail[j];
while(k&&t[i][k+1]!=t[i][j+1])
k=fail[k];
fail[j+1]=t[i][k+1]==t[i][j+1]?k+1:0;
}
int j=0;
for(int k=1;k<=n;++k)
{
while(j&&t[i][j+1]!=s[k])
j=fail[j];
if(t[i][j+1]==s[k])
++j;
if(j==len[i])
++cnt[i],b[i][cnt[i]].l=k-len[i]+1,b[i][cnt[i]].r=k;
}
sort(b[i]+1,b[i]+cnt[i]+1);
// for(int j=1;j<=cnt[i];++j)
// cerr<<j<<" l="<<b[i][j].l<<" r="<<b[i][j].r<<endl;
}
printf("%d %d\n",MIN::solve(),MAX::solve());
}
return 0;
}

BZOJ4560 [JLoi2016]字符串覆盖的更多相关文章

  1. [BZOJ4560][JLOI2016]字符串覆盖(贪心+DP)

    先用KMP求出所有可以放的位置,然后两个值分别处理. 最大值: 贪心,4!枚举放的先后位置顺序,2^3枚举相邻两个串是否有交. 若有交,则后一个的起始位置一定是离前一个的结束位置最近的位置,无交也一样 ...

  2. BZOJ4560 JLOI2016字符串覆盖(kmp+贪心+状压dp+单调队列)

    首先kmp求出每个子串能放在哪些位置.接下来的两部分贪心和状压都可以,各取比较方便的. 最大值考虑贪心.考虑枚举子串的左端点出现顺序,在此基础上每个子串的位置肯定都应该尽量靠前,有是否与上个子串有交两 ...

  3. 【BZOJ4560】[JLoi2016]字符串覆盖 KMP+状压DP

    [BZOJ4560][JLoi2016]字符串覆盖 Description 字符串A有N个子串B1,B2,…,Bn.如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)这样A中的若 ...

  4. 并不对劲的bzoj4560:p3269:[JLOI2016]字符串覆盖

    题目大意 \(T\)(\(T\leq10\))组询问 每组询问给出一个字符串\(A\)(\(|A|\leq10^4\)),\(n\)(\(n\leq4\))个\(A\)的子串\(B_1,B_2,B_3 ...

  5. 【BZOJ】4560: [JLoi2016]字符串覆盖

    题解 先用kmp求出来一个ed[i][j]表示在母串的第i位是第j个子串的结尾 考虑状压一个二进制位表示这个子串覆盖过没有 对于最大值,记一个dp[S][i]表示子串的使用状况为S,当前为母串的第i位 ...

  6. 笔记:iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)(别人的代码直接复制过来的,我脸皮有点厚)

    NSString* str=@"hello";//存在代码区,不可变 NSLog(@"%@",str); //1.[字符串插入] NSMutableString ...

  7. iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)

    NSString* str=@"hello";//存在代码区,不可变 NSLog(@"%@",str); //1.[字符串插入] NSMutableString ...

  8. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  9. 贪心/构造/DP 杂题选做Ⅱ

    由于换了台电脑,而我的贪心 & 构造能力依然很拉跨,所以决定再开一个坑( 前传: 贪心/构造/DP 杂题选做 u1s1 我预感还有Ⅲ(欸,这不是我在多项式Ⅱ中说过的原话吗) 24. P5912 ...

随机推荐

  1. 20145217《网络对抗》 逆向及BOF进阶实践学习总结

    20145217<网络对抗> 逆向及BOF进阶实践学习总结 实践目的 1.注入shellcode 2.实现Return-to-libc攻击 知识点学习总结 Shellcode实际是一段代码 ...

  2. COS-4进程及进程管理

    操作系统是用户和计算机的接口,同时也是计算机硬件和其他软件的接口.操作系统的功能包括管理计算机系统的硬件.软件及数据资源,控制程序运行,改善人机界面,为其它应用软件提供支持,让计算机系统所有资源最大限 ...

  3. maven说明

    1.maven 仓库地址 http://mvnrepository.com/ 2.maven jar包搜索地址 http://search.maven.org/ 3. 点开上面的 版本链接,就可以看到 ...

  4. 全卷积网络(FCN)与图像分割

    最近在做物体检测,也用到了全卷积网络,来此学习一波. 这篇文章写了很好,有利于入门,在此记录一下: http://blog.csdn.net/taigw/article/details/5140144 ...

  5. JMS-activMq与spring进行整合

     对JMS做了一个简要介绍之后,接下来就讲一下Spring整合JMS的具体过程.JMS只是一个标准,真正在使用它的时候我们需要有它的具体实现,这里我们就使用Apache的activeMQ来作为它的实现 ...

  6. scala学习手记30 - 闭包

    首先要弄白闭包的概念. 教材中的说法是:闭包是一种特殊的函数值,闭包中封闭或绑定了在另一个作用域或上下文中定义的变量.这里说闭包是一种特殊的函数值. 维基百科中的说法是:在计算机科学中,闭包(英语:C ...

  7. Mongodb笔记(三)user && aggregate && mapReduce

    版本:mongodb3.4. User: mongodb使用验证登录:默认不开启,mongod中使用--auth开启:  mongod -port=3000 --auth  : 基本方法: db.cr ...

  8. lightoj1370欧拉函数/素数筛

    这题有两种解法,1是根据欧拉函数性质:素数的欧拉函数值=素数-1(可根据欧拉定义看出)欧拉函数定义:小于x且与x互质的数的个数 #include<map> #include<set& ...

  9. cenOs7安装redis

    1.安装redis redis 目前没有官方 RPM 安装包,我们需要从源代码编译,而为了要编译就需要安装 Make 和 GCC. 如果没有安装过 GCC 和 Make,那么就使用 yum 安装 -- ...

  10. Easyui 行编辑

    之前没用过,突然用了的时候手忙脚乱的感觉  找了官方文档也好 百度了一大堆东西   表示个人脑袋跟不上思路 直接铺上简化的  以后自己 找起来也方便  以下代码已经执行 应该不会再错了 <tab ...