题目大意

\(T\)(\(T\leq10\))组询问

每组询问给出一个字符串\(A\)(\(|A|\leq10^4\)),\(n\)(\(n\leq4\))个\(A\)的子串\(B_1,B_2,B_3,...,B_n\)(\(\forall i \in[1,n],|B_i|\leq10^3\))

如果\(|B_i|=r-l+1\)且\(B_i\)的每一个字符依次与\(A_l,A_{l+1},...,A_{r}\)相等,那么区间\([l,r]\)每个位置都被覆盖了一次

求如果必须把\(B_1,...,B_n\)全部用上,那么字符串\(A\)至少有几个位置被覆盖不少于一次?至多有几个位置被覆盖不少于一次?

题解

至少:

如果一个串\(B_i\)是\(B_j\)的子串,那么让\(B_i\)覆盖被\(B_j\)覆盖的部分会更优,所以先去掉“是别的串的子串的串”

设\(f(S,j)\)表示已经用过的子串集合为\(S\),目前覆盖的最靠右的位置为\(j\)

因为已经去掉了“是别的串的子串的串”,所以以\(j\)为结尾的\(B\)串只有一个,假设它是\(B_x\)

有两种情况:1.在选取\(B_x\)前(此时已用过的子串集合为\(S\bigoplus\{ x\}\)),被覆盖的区域与\(B_x\)有交;2.在选取\(B_x\)前,被覆盖的区域与\(B_x\)没有交

1.\(f(S,j)=min\{ f(S\bigoplus\{ x\},k)+j-k \mid j-|B_x|+1\leq k\leq j\}\)因为已经去重,所以集合\(S\bigoplus\{ x\}\)里最靠后的子串的起点一定不会比\(j-|B_x|+1\)更靠右,加入\(B_x\)后新覆盖的区域为\(j-k\)

因为没有“是别的串的子串的串”,所以当\(j\)右移时\(j-|B_x|+1\)也右移,用单调队列维护

2.\(f(S,j)=min\{ f(S\bigoplus\{ x\},k) \mid 0\leq k\leq j-|B_x|+1\}+|B_x|\),维护前缀最小值就行

时间复杂度\(\Theta(2^n*|A|*n)\)或\(\Theta(2^n*|A|)\)

至多:

发现并不能去重

暴力枚举\(n\)个串的顺序,暴力枚举每个串与之前的部分是否有交

有交就让交的部分尽可能小,没有交就尽可能把这个串往前放

时间复杂度大概\(\Theta(n!*2^n*n)\)

代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 10010
#define maxm 1010
#define pii pair<int,int>
#define fi first
#define se second
#define mp(x,y) make_pair(x,y)
#define maxs (1<<5)
#define chmn(x,y) x=min(x,y)
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
return;
}
int T,pos[5][maxn],n,lens,lent[5],fa[5][maxm],ord[20],ord2[20],vis[5],f[maxs][maxn],maxans,minans,cntnd,hsh[5],ext[5][maxn],pref[maxs][maxn];
char t[5][maxm],s[maxn];
pii a[20];
struct deque
{
int w[maxn],tim[maxn],hd,tl;
void reset(){hd=1,tl=0;}
void push(int x,int y){while(hd<=tl&&w[tl]>=x)tl--;w[++tl]=x,tim[tl]=y;}
void del(int y){while(hd<=tl&&tim[hd]<y)hd++;}
int empty(){return hd>tl?1:0;}
}q[maxs];
bool cmp1(int x,int y){return lent[x]<lent[y];}
bool cmp2(int x,int y){return a[x].fi==a[y].fi?a[x].se<a[y].se:a[x].fi<a[y].fi;}
int calmx()
{
rep(i,1,cntnd)ord2[i]=i;
sort(ord2+1,ord2+cntnd+1,cmp2);
int now=0,lenth=0;
rep(i,1,cntnd)
{
now+=a[ord2[i]].se;
if(i==cntnd||a[ord2[i+1]].fi!=a[ord2[i]].fi){if(now)lenth+=a[ord2[i+1]].fi-a[ord2[i]].fi;}
}
return lenth;
}
int getpla(int step,int maxp)
{
if(step==n+1){return calmx();}
int now=ord[step],res=0;
int l=1,r=pos[now][0],ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
if(pos[now][mid]-lent[now]+1<=maxp){l=mid+1;ans=max(ans,mid);}
else r=mid-1;
}
if(ans!=0&&pos[now][ans]>=maxp)a[++cntnd]=mp(pos[now][ans]+1,-1),a[++cntnd]=mp(pos[now][ans]-lent[now]+1,1),res=getpla(step+1,pos[now][ans]),cntnd-=2;
if(ans+1!=pos[now][0]+1&&pos[now][ans+1]>=maxp)
a[++cntnd]=mp(pos[now][ans+1]+1,-1),a[++cntnd]=mp(pos[now][ans+1]-lent[now]+1,1),
res=max(res,getpla(step+1,pos[now][ans+1])),cntnd-=2;
return res;
}
void getord(int step)
{
if(step==n+1){cntnd=0;maxans=max(maxans,getpla(1,0));return;}
rep(i,1,n)if(!vis[i])vis[i]=1,ord[step]=i,getord(step+1),vis[i]=0;
return;
}
int main()
{
T=read();
while(T--)
{
scanf("%s",s+1);lens=strlen(s+1);maxans=0;
n=read();memset(ext,0,sizeof(ext));
rep(i,1,n)
{
pos[i][0]=vis[i]=0,scanf("%s",t[i]+1),lent[i]=strlen(t[i]+1);
fa[i][0]=-1,fa[i][1]=0;
rep(j,2,lent[i])
{
int k=fa[i][j-1];
while(k&&t[i][k+1]!=t[i][j])k=fa[i][k];
if(t[i][k+1]==t[i][j])fa[i][j]=k+1;
else fa[i][j]=0;
}
int k=0;
rep(j,1,lens)
{
while(k&&(k==lent[i]||t[i][k+1]!=s[j]))k=fa[i][k];
if(t[i][k+1]==s[j]){k++;if(k==lent[i])pos[i][++pos[i][0]]=j,ext[i][j]=1;}
else k=0;
}
}
getord(1);
rep(i,1,n)ord[i]=i,vis[i]=0;
sort(ord+1,ord+n+1,cmp1);
rep(i,1,n)
rep(j,i+1,n)
{
int fl=0,k=0;
rep(l,1,lent[ord[j]])
{
while(k&&(t[ord[i]][k+1]!=t[ord[j]][l]))k=fa[ord[i]][k];
if(t[ord[i]][k+1]==t[ord[j]][l]){k++;if(k==lent[ord[i]]){fl=1;break;}}
else k=0;
}
vis[ord[i]]|=fl;
}
cntnd=0;
rep(i,1,n)if(!vis[i])hsh[++cntnd]=i;
int fulls=(1<<cntnd)-1;
rep(i,0,fulls)q[i].reset();
memset(f,0x7f,sizeof(f)),memset(pref,0x7f,sizeof(pref));
int inf=minans=f[0][0];f[0][0]=pref[0][0]=0;q[0].push(0,0);
rep(j,1,lens)rep(s,0,fulls)
{
int num=0;pref[s][j]=pref[s][j-1];
if(!s)continue;
rep(i,1,cntnd)
{
if(ext[hsh[i]][j]&&((1<<(i-1))&s))
{
num++;
int pres=s^(1<<(i-1));
q[pres].del(j-lent[hsh[i]]);
if(!q[pres].empty())f[s][j]=min(f[s][j],q[pres].w[q[pres].hd]+j);
if(j-lent[hsh[i]]>=0)f[s][j]=min(f[s][j],pref[pres][j-lent[hsh[i]]]+lent[hsh[i]]);
pref[s][j]=min(pref[s][j],f[s][j]);if(f[s][j]!=inf)q[s].push(f[s][j]-j,j);
}
}
}
rep(j,1,lens)chmn(minans,f[fulls][j]);
write(minans),putchar(' '),write(maxans),putchar('\n');
}
return 0;
}
/*
2
hello
4
he
l
l
o
abacaba
4
ab
ba
a
c
*/
一些感想

片手玩得好不是大佬,笛玩得好才是。——吹笛子时被车的片手流

并不对劲的bzoj4560:p3269:[JLOI2016]字符串覆盖的更多相关文章

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

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

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

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

  3. BZOJ4560 [JLoi2016]字符串覆盖

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

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

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

  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. 并不对劲的p3709:大爷的字符串题

    题目大意 区间众数 题解 莫队 代码 #include<algorithm> #include<cmath> #include<cstdio> #include&l ...

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

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

随机推荐

  1. zoj 1763 A Simple Question of Chemistry

    A Simple Question of Chemistry Time Limit: 2 Seconds      Memory Limit: 65536 KB Your chemistry lab ...

  2. Food Delivery (区间DP)

    When we are focusing on solving problems, we usually prefer to stay in front of computers rather tha ...

  3. SQL ROW_NUMBER() 通用分页存储过程

    --提取分页数据,返回总记录数 Create procedure [dbo].[sp_Common_GetDataPaging_ReturnDataCount] ( @SqlString varcha ...

  4. SkyWalking 分布式追踪系统

    随着微服务架构的流行,一些微服务架构下的问题也会越来越突出,比如一个请求会涉及多个服务,而服务本身可能也会依赖其他服务,整个请求路径就构成了一个网状的调用链,而在整个调用链中一旦某个节点发生异常,整个 ...

  5. 【POJ3294】Life Forms(后缀数组,二分)

    题意: n<=100 len[i]<=1000 思路:这是一道论文题 ..]of longint; ch:..]of ansistring; n,n1,l,r,mid,last,i,j,m ...

  6. python学习之 - 时间模块

    时间模块模块名:time时间模块的转换流程图. UTC:英国格林威治时间.时间戳作用:是用来进行时间计算的.进行加减时间.注意:时间计算是用秒为单位time.process_time():测量处理器运 ...

  7. 洛谷——P2865 [USACO06NOV]路障Roadblocks

    P2865 [USACO06NOV]路障Roadblocks 题目描述 Bessie has moved to a small farm and sometimes enjoys returning ...

  8. POJ 3230 【DP】

    题意: 某货旅行,在n个城市呆m天. 给出从第i个城市到第j个城市的路费,或者留在某个城市的生活费. 给出在第i天在第j个城市的收益. 可以在城市之间任意穿梭逗留没有其他特殊要求. 求收益最大是多少. ...

  9. 携程Apollo(阿波罗)配置中心Spring Boot迁移日志组件,使用配置中心进行管理的思路

    说明: 1.Spring Boot项目默认使用logback进行日志管理 2.logback在启动时默认会自动检查是否有logback.xml文件,如果有时会有限加载这个文件. 3.那么如果是用配置中 ...

  10. HDTVI,HDCVI

    HDTVI是海康的同轴高清标准,HDCVI是大华的同轴高清标准,二者互相不兼容.