【BZOJ4560】[JLoi2016]字符串覆盖

Description

字符串A有N个子串B1,B2,…,Bn。如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)这样A中的若干字符就被这N个子串覆盖了。问A中能被覆盖字符个数的最小值和最大值。

Input

第一行包含一个正整数T,表示数据组数。保证T≤10。接下来依次描述T组数据,每组数据中:第一行包含一个由小写字母组成的字符串,表示母串A。第二行包含一个整数N,表示子串的个数。接下来N行,每行包含一个由小写字母组成的字符串,描述子串。数据保证所有子串均在母串中出现。字符串长度A<=10000,N<=4,子串长充<=1000

Output

输出为T行,对应每组数据的答案。每行包含两个整数Minans和Maxans,分别表示对应数据中能被覆盖字符数量的最小值和最大值。

Sample Input

2
hello
4
he
l
l
o
abacaba
4
ab
ba
a
c

Sample Output

4 5
4 6

题解:网上题解说是某贪心?然而我只会状压DP。

先用KMP求出每个子串的所有出现位置,然后用f[i][j]表示前i个字符,子串的出现情况为j的覆盖数量最大值,g[i][j]表示最小值。转移时需要用到树状数组。

然而状压的做法似乎好多情况都无法处理,如子串存在包含的情况。。。总之加了一坨特判就过了,可能会被hack。

代码不可读。

#include <cstdio>
#include <cstring>
#include <iostream>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=10010;
int n,m;
int len[5],vis[5],ch[maxn],next[maxn];
char S[maxn],T[5][maxn];
int f[maxn][16],g[maxn][16],sf[maxn][16],sg[maxn][16];
struct node
{
int s[maxn],typ;
inline int cmp(int a,int b)
{
return ((a>b)^typ)?a:b;
}
inline void updata(int x,int v)
{
x++;
for(int i=x;i;i-=i&-i) s[i]=cmp(s[i],v);
}
inline int query(int x)
{
x++;
int i,ret=s[0];
for(i=x;i<=m+1;i+=i&-i) ret=cmp(ret,s[i]);
return ret;
}
}s1[16],s2[16];
void work()
{
scanf("%s",S),m=strlen(S);
scanf("%d",&n);
int i,j,k,a,b,tmp=(1<<n)-1;
memset(ch,0,sizeof(ch));
for(i=1;i<=n;i++)
{
memset(next,0,sizeof(next));
scanf("%s",T[i]),len[i]=strlen(T[i]);
next[0]=k=-1,j=0;
while(j<len[i])
{
if(k==-1||T[i][j]==T[i][k]) next[++j]=++k;
else k=next[k];
}
j=k=0;
while(j<m)
{
if(k==-1||S[j]==T[i][k]) j++,k++;
else k=next[k];
if(k==len[i]) ch[j-len[i]]|=1<<(i-1);
}
}
memset(vis,0,sizeof(vis));
for(a=1;a<=n;a++) for(b=1;b<=n;b++) if(a!=b&&!vis[a]&&!vis[b])
{
memset(next,0,sizeof(next));
next[0]=k=-1,j=0;
while(j<len[a])
{
if(k==-1||T[a][j]==T[a][k]) next[++j]=++k;
else k=next[k];
}
j=k=0;
while(j<len[b])
{
if(k==-1||T[b][j]==T[a][k]) j++,k++;
else k=next[k];
if(k==len[a])
{
vis[a]=1;
break;
}
}
}
for(i=1;i<=n;i++) if(vis[i]) tmp^=1<<(i-1);
memset(s1,0xc0,sizeof(s1)),memset(s2,0x3f,sizeof(s2));
memset(sf,0xc0,sizeof(sf)),memset(sg,0x3f,sizeof(sg));
memset(f,0xc0,sizeof(f)),memset(g,0x3f,sizeof(g));
for(i=0;i<1<<n;i++) s1[i].typ=0,s2[i].typ=1,f[0][i]=-1<<30,g[0][i]=1<<30;
for(i=0;i<=m;i++)
{
for(j=0;j<1<<n;j++)
{
if(!i)
{
if(!j) f[0][0]=g[0][0]=sf[0][0]=sg[0][0]=0;
else f[0][j]=s1[j].query(i)+i,g[0][j]=s2[j].query(i)+i;
}
else
{
sf[i][j]=max(sf[i][j],sf[i-1][j]),sg[i][j]=min(sg[i][j],sg[i-1][j]);
f[i][j]=max(sf[i][j],s1[j].query(i)+i),g[i][j]=min(sg[i][j],s2[j].query(i)+i);
}
for(k=1;k<=n;k++) if(!((j>>(k-1))&1)&&((ch[i]>>(k-1))&1))
{
s1[j|(1<<(k-1))].updata(i+len[k],f[i][j]-i);
sf[i+len[k]][j|(1<<(k-1))]=max(sf[i+len[k]][j|(1<<(k-1))],f[i][j]+len[k]);
if(!vis[k])
{
s2[j|(1<<(k-1))].updata(i+len[k],g[i][j]-i);
sg[i+len[k]][j|(1<<(k-1))]=min(sg[i+len[k]][j|(1<<(k-1))],g[i][j]+len[k]);
}
}
}
}
int ans=0;
for(i=1;i<1<<n;i++) if((i&tmp)==tmp) ans=max(ans,f[m][i]);
printf("%d %d\n",g[m][tmp],ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--) work();
return 0;
}//2 hello 4 he l l o abacaba 4 ab ba a c
 

【BZOJ4560】[JLoi2016]字符串覆盖 KMP+状压DP的更多相关文章

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

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

  2. BZOJ4560 [JLoi2016]字符串覆盖

    题意 字符串A有N个子串B1,B2,-,Bn.如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠) 这样A中的若干字符就被这N个子串覆盖了.问A中能被覆盖字符个数的最小值和最大值. ...

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

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

  4. 瓷砖覆盖(状压DP)

    题目描述 Description 用1*2的瓷砖去铺N*M的地面,问有多少种铺法 输入描述 Input Description 第一行有两数n,m.表示地面的大小 输出描述 Output Descri ...

  5. HDU 1565 - 方格取数(1) - [状压DP][网络流 - 最大点权独立集和最小点权覆盖集]

    题目链接:https://cn.vjudge.net/problem/HDU-1565 Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32 ...

  6. POJ2411骨牌覆盖——状压dp

    题目:http://poj.org/problem?id=2411 状压dp.注意一下代码中标记的地方. #include<iostream> #include<cstdio> ...

  7. FJNU Fang G and his Friends(状压DP)题解

    Description     众所周知,fang G 有很多小伙伴,有一天,Fang G 打算带他们去玩有趣的游戏OOXX,这个游戏需要分成两组,有趣的是,每个人互相之间都有一个满意度,大家都想和自 ...

  8. HDU3247 Resource Archiver (AC自动机+spfa+状压DP)

    Great! Your new software is almost finished! The only thing left to do is archiving all your n resou ...

  9. fzu2188 状压dp

    G - Simple String Problem Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%I64d & ...

随机推荐

  1. 修改easyui panel 默认样式

    有这么个需求需要修改easyui panel头部中的背景色.于是根据panel中的最终被浏览器解析出来的类名,直接修改这个css样式,设置backgroud-color这个属性,发现不管用. 于是,就 ...

  2. Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用

    一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...

  3. 现在的C语言编辑器里的int范围为什么是-2147483648~2147483647 2014-08-05 10:21 100人阅读 评论(0) 收藏

    下面是引用百度文库的一段话: "这得从二进制的原码说起: 如果以最高位为符号位,二进制原码最大为0111111111111111=215-1=32767 最小为111111111111111 ...

  4. Spring Boot(三):logback打印日志

    springboot对logback的支持是非常好的,不需要任何配置,只需要在resource下加logback.xml就可以实现功能直接贴代码: <?xml version="1.0 ...

  5. emacs 中使用git diff命令行

    在shell中执行git diff命令,常常会看到例如以下警告信息: terminal is not fully functional 事实上非常easy,配置一下就可以. git config -- ...

  6. C++语言基础(23)-拷贝构造函数

    当以拷贝的方式初始化一个对象时,会调用一个特殊的构造函数,就是拷贝构造函数(Copy Constructor). 例如: #include <iostream> #include < ...

  7. sql注入的防御和挖掘

    首先我们可以在PHP.ini当中将display_errror关闭,以防止报错型的注入. 1.字符型防护 is_number 正则来判断是否是数字. ctype_digit() intval() st ...

  8. ajax 传递参数中文乱码解决办法

    /********Start***********/ /*获取地址栏参数*/ function getRequest(){ var url = location.search; //获取url中&qu ...

  9. 目录视图摘要视图订阅 基于Extjs开发不允许为空的文本框提示及相应的验证错误提示(转)

    原文地址:http://blog.csdn.net/kunoy/article/details/8007585 本文主要解决问题: 1.区分哪些文本框不允许为空,很多网站都采用在文本框后加*号,ext ...

  10. IT 服务管理工具 iTop

    iTop,作为全面支持ITIL流程的一款ITSM工具,具有强大的ITSM功能,开源免费.简单易用. iTop,即IT运营门户(IT Operation Portal),是一个开源web应用程序,用于I ...