并不对劲的bzoj4560:p3269:[JLOI2016]字符串覆盖
题目大意
\(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]字符串覆盖的更多相关文章
- 【BZOJ4560】[JLoi2016]字符串覆盖 KMP+状压DP
[BZOJ4560][JLoi2016]字符串覆盖 Description 字符串A有N个子串B1,B2,…,Bn.如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠)这样A中的若 ...
- [BZOJ4560][JLOI2016]字符串覆盖(贪心+DP)
先用KMP求出所有可以放的位置,然后两个值分别处理. 最大值: 贪心,4!枚举放的先后位置顺序,2^3枚举相邻两个串是否有交. 若有交,则后一个的起始位置一定是离前一个的结束位置最近的位置,无交也一样 ...
- BZOJ4560 [JLoi2016]字符串覆盖
题意 字符串A有N个子串B1,B2,-,Bn.如果将这n个子串分别放在恰好一个它在A中出现的位置上(子串之间可以重叠) 这样A中的若干字符就被这N个子串覆盖了.问A中能被覆盖字符个数的最小值和最大值. ...
- BZOJ4560 JLOI2016字符串覆盖(kmp+贪心+状压dp+单调队列)
首先kmp求出每个子串能放在哪些位置.接下来的两部分贪心和状压都可以,各取比较方便的. 最大值考虑贪心.考虑枚举子串的左端点出现顺序,在此基础上每个子串的位置肯定都应该尽量靠前,有是否与上个子串有交两 ...
- 【BZOJ】4560: [JLoi2016]字符串覆盖
题解 先用kmp求出来一个ed[i][j]表示在母串的第i位是第j个子串的结尾 考虑状压一个二进制位表示这个子串覆盖过没有 对于最大值,记一个dp[S][i]表示子串的使用状况为S,当前为母串的第i位 ...
- 笔记:iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)(别人的代码直接复制过来的,我脸皮有点厚)
NSString* str=@"hello";//存在代码区,不可变 NSLog(@"%@",str); //1.[字符串插入] NSMutableString ...
- iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)
NSString* str=@"hello";//存在代码区,不可变 NSLog(@"%@",str); //1.[字符串插入] NSMutableString ...
- 并不对劲的p3709:大爷的字符串题
题目大意 区间众数 题解 莫队 代码 #include<algorithm> #include<cmath> #include<cstdio> #include&l ...
- 贪心/构造/DP 杂题选做Ⅱ
由于换了台电脑,而我的贪心 & 构造能力依然很拉跨,所以决定再开一个坑( 前传: 贪心/构造/DP 杂题选做 u1s1 我预感还有Ⅲ(欸,这不是我在多项式Ⅱ中说过的原话吗) 24. P5912 ...
随机推荐
- Linux 磁盘测速
读: time dd if=/dev/zero of=/test.dbf bs=8k count=1000000 写: time dd if=/dev/zero of=/var/test bs=8k ...
- [luoguP2184] 贪婪大陆(树状数组)
传送门 用两个树状数组,cr 维护 1....x 中 r 的数量 cl 维护 1....x 中 l 的数量 求答案的时候只需要求 y 前面 被作为左端点 的个数 - x 前面 被作为右端点的个数 —— ...
- hdu 2063最大匹配
#include<stdio.h> #include<string.h> int link[600],mark[600],map[600][600],m,n; int find ...
- Java 实体-实体的映射框架
一.Object mapping 的技术分类: 运行期 反射调用set/get 或者是直接对成员变量赋值 . 该方式通过invoke执行赋值 *,实现时一般会采用beanutil, Javassist ...
- [Vijos] 河蟹王国
描述 河蟹王国有一位河蟹国王,他的名字叫羊驼.河蟹王国富饶安定,人们和谐相处.有一天,羊驼国王心血来潮,想在一部分人中挑出最和谐的人.于是,羊驼国王将他的子民排成了一列(==!!b汗~好长呀).每个人 ...
- Codevs 3556 科技庄园==洛谷 P2760
时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description Life是codevs的用户,他是一个道德极高的用户,他积极贯彻党的十八大精神, ...
- HDU 6035 (虚树)(统计颜色)
HDU 6035 Colorful Tree Problem : 给一棵树,每个结点有一种颜色,定义每条路径的权值为这条路径上颜色的种数,询问所有路径(C(n,2)条)的权值之和. Solution ...
- QueenAttack
问题分析: 1.对于行和列,要求得每行或每列的棋子个数,只需要把横坐标或纵坐标相同的棋子数目相加即可,然后使用k*(k-1)/2就可求得攻击次数 2.对于对角线上的点,需要分析对角线上点的坐标关系.分 ...
- FatMouse's Speed--hdu1160(dp+输出路径)
Problem Description FatMouse believes that the fatter a mouse is, the faster it runs. To disprove th ...
- Piggy-Bank--hdu1114(完全背包)
http://acm.hdu.edu.cn/showproblem.php?pid=1114 Problem Description Before ACM can do anything, a bud ...