zoukankan      html  css  js  c++  java
  • [JLoi2016]字符串覆盖

    题意

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

    字符串长度$A<=10000,N<=4$,子串长充$<=1000$

    分析

    参照_Gloid的题解。

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

    最大值

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

    最小值

    考虑状压。首先把被包含的子串去掉方便处理。将线段排序,设$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
    ",MIN::solve(),MAX::solve());
    	}
    	return 0;
    }
    
  • 相关阅读:
    std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(5)
    std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(4)
    C++多线程库的常用函数 std::lock()
    std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(3)
    std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(2)
    std::get<C++11多线程库~线程间共享数据>(10):使用互斥量保护共享数据(1)
    C++多线程库的常用模板类 std::lock_guard
    C++多线程库的常用类 std::mutex
    npm install 时,下载github的包超时解决方法
    SAP 电商云 Spartacus UI 和路由相关的 State 处理
  • 原文地址:https://www.cnblogs.com/autoint/p/10325773.html
Copyright © 2011-2022 走看看