[JLOI2016]字符串覆盖

Posted by Dispwnl on April 26, 2019

题目

题解

对于求最大值的情况,如果确定子串出现顺序的话,贪心即可,即最优情况把当前串放在上一个串结尾后面第一个出现位置,否则放在上一个串结尾前面第一个出现位置

对于求最小值的情况,设$f_{i,j}$表示转移到低$i$个字符子串出现状态为$j$(去重后)的最小值,设当前串为$S$,则有式子$f_{i,j}=\min(f_{a,j-S},f_{b,j-S}),i-len_S+1\le a\le i,1\le b\le i-len_S$

直接线段树维护即可其实可以单调队列

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<vector>
# include<algorithm>
# define tl (k<<1)
# define tr (k<<1|1)
# define mid (l+r>>1)
using namespace std;
const int MAX=1e4+5,inf=1e9;
struct p{
	int x,x_;
}s[16][MAX<<2];
int T,n,N,cnt,mx;
int nxt[MAX],pos[MAX<<2],len[4],id[4],re[4];
char a[MAX],b[MAX];
bool use[4];
vector<int> vec[4],Vec[MAX];
void GET_NXT(int n)
{
	int k=0;
	memset(nxt,0,sizeof(nxt));
	for(int i=2;i<=n;++i)
	  {
	  	while(k>0&&b[k+1]!=b[i]) k=nxt[k];
	  	if(b[k+1]==b[i]) ++k;
	  	nxt[i]=k;
	  }
}
void KMP(int id,int m)
{
	int k=0;
	for(int i=1;i<=n;++i)
	  {
	  	while(k>0&&b[k+1]!=a[i]) k=nxt[k];
	  	if(b[k+1]==a[i]) ++k;
	  	if(k==m)
		  vec[id].push_back(pos[++cnt]=((i=i-m+1)+m-1)),Vec[pos[cnt]].push_back(id),k=0;
	  }
}
void pus(int k,p *s) {s[k].x=min(s[tl].x,s[tr].x),s[k].x_=min(s[tl].x_,s[tr].x_);}
void change(int l,int r,int k,int x,int d,p *s)
{
	if(l==r) return void(s[k].x=d);
	if(x<=mid) change(l,mid,tl,x,d,s);
	else change(mid+1,r,tr,x,d,s);
	pus(k,s);
}
void change_(int l,int r,int k,int x,int d,p *s)
{
	if(l==r) return void(s[k].x_=d);
	if(x<=mid) change_(l,mid,tl,x,d,s);
	else change_(mid+1,r,tr,x,d,s);
	pus(k,s);
}
int ask(int l,int r,int k,int L,int R,p *s)
{
	if(r<L||R<l) return 1e9;
	if(l>=L&&r<=R) return s[k].x;
	return min(ask(l,mid,tl,L,R,s),ask(mid+1,r,tr,L,R,s));
}
int ask_(int l,int r,int k,int L,int R,p *s)
{
	if(r<L||R<l) return 1e9;
	if(l>=L&&r<=R) return s[k].x_;
	return min(ask_(l,mid,tl,L,R,s),ask_(mid+1,r,tr,L,R,s));
}
int look(int x,int id)
{
	int l=0,r=vec[id].size()-1,ans=-1;
	while(l<=r)
	{
		if(vec[id][mid]-len[id]+1>x) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
int look1(int x,int id)
{
	int l=0,r=vec[id].size()-1,ans=-1;
	while(l<=r)
	{
		if(vec[id][mid]-len[id]+1<=x) ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans;
}
int solve()
{
	int ans=0;
	for(int i=0,x=0,l,r;i<N;++i)
	  if(!x) x=vec[id[i]][0],ans=len[id[i]];
	  else
	  {
	  	r=look(x,id[i]);
	  	if(r!=-1) x=vec[id[i]][r],ans+=len[id[i]];
	  	else l=look1(x,id[i]),ans+=max(vec[id[i]][l]-x,0),x=max(x,vec[id[i]][l]);
	  }
	return ans;
}
void dfs(int x=1)
{
	if(x==N+1) return void(mx=max(mx,solve()));
	for(int i=0;i<N;++i)
	  if(!use[i]) use[i]=1,id[x-1]=i,dfs(x+1),use[i]=0;
}
void Solve() {return void(dfs());}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s%d",a+1,&N),n=strlen(a+1),mx=0,cnt=0;
		memset(s,127,sizeof(s));
		memset(use,0,sizeof(use));
		memset(vec,0,sizeof(vec));
		memset(Vec,0,sizeof(Vec));
		for(int i=0;i<N;++i)
		  scanf("%s",b+1),GET_NXT(len[i]=strlen(b+1)),KMP(i,len[i]);
		Solve();
		int M=N,o=0;
		for(int i=0;i<N;++i)
		  {
		  	bool fl=0;
		  	for(int j=0;j<N&&!fl;++j)
			  if(i!=j&&len[i]<len[j]) for(int k:vec[i])
				if(k<=vec[j][0]&&k-len[i]+1>=vec[j][0]-len[j]+1) {--M,fl=1;break;}
			if(!fl) id[o]=i,re[i]=o++,use[i]=1;
		  }
		for(int i=0;i<M;++i)
		  for(int v:vec[id[i]])
			change(1,n,1,v,len[id[i]],s[1<<i]),change_(1,n,1,v,len[id[i]]-v,s[1<<i]);
		sort(pos+1,pos+1+cnt),cnt=unique(pos+1,pos+1+cnt)-pos-1;
		for(int i=0;i<(1<<M);++i)
		  for(int j=1,x;j<=cnt;++j)
			{
				x=1e9;
				for(int v:Vec[pos[j]])
		 		  if(use[v]&&(i&(1<<re[v]))&&i>(1<<re[v])) 
				   x=min(x,min(ask_(1,n,1,pos[j]-len[v]+1,pos[j],s[i-(1<<re[v])])+pos[j],ask(1,n,1,1,pos[j]-len[v],s[i-(1<<re[v])])+len[v]));
		 		if(x<1e9) change(1,n,1,pos[j],x,s[i]),change_(1,n,1,pos[j],x-pos[j],s[i]);
			}
		printf("%d %d\n",s[(1<<M)-1][1].x,mx);
	}
	return 0;
}