[ARC058F] 文字列大好きいろはちゃん

题意

给定 n 个字符串 si,你需要选择若干个字符串按从前往后的顺序拼起来使得总长为 k 且字典序最小,保证有解。

n2000,k104,|si|106

分析

先考虑一个显然的暴力 DP:设 fi,j 表示前 i 个串总长为 j 的最小字典序(注意这里由于限制了长度所以无需倒序转移),复杂度 O(nk2),无法通过。

考虑优化,发现很多状态实际上是不会成为最优状态的。

首先做一个简单的小剪枝:预处理 gi,j 表示用 [i,n] 中的串能否拼出长为 k 的字符串,计算的复杂度是 O(nk2w),那么我们在转移 fi,j 时如果 gi+1,kj=0 那么状态 fi,j 不合法,必然不会成为最优状态。

因为我们需要的是字典序最小,而如果一个方案在转移前就存在某一位比另一个方案大,那么前一种方案就是没用的。不难发现,对于一个 i,所有可能成为最优状态的 fi, 互为前缀关系。把长度最大的那个 fi, 取出来,设为 ti,那么所有的 fi, 都是 ti 的一段前缀。那么在 i 之下的所有合法状态均可以用一个数 j 表示。

考虑转移。把 fi1, 的合法状态压成一个单调栈,从顶到底长度递降排序。令 bj=fi1,j+si,显然 bi 的数量等于栈大小。考虑把一个 bi 压进栈,若 bi 的字典序比栈顶的字典序要大,那么这个 bi 就废了;若比栈顶小,那么弹出栈顶,直到栈顶是 bi 的前缀或者栈为空(注意不会发生比栈顶字典序大的情况,否则会在初始被废掉)。

现在我们就把时间复杂度降为了 O(nkT(|S|)),其中 T(|S|) 是处理字符串比较的时间复杂度。发现我们在比较字符串字典序大小时比较的是 fi,+b 之间的大小关系,后面的 b 可以为空。设要比较的是 s1=fi,x+bis2=fi,y+bi,设 x<y,由于 fi,ti 的一段前缀,所以在 [1,x] 这段前缀上两串相等,问题转化为 biti[x+1,y] 比较大小,若两者相等再比较 bi[yx,]bi 的大小。发现这就是 Z 函数模板题(求字符串与本身每个后缀和另一个串的每个后缀的 LCP)。使用 Z 函数做到 T(|S|)=O(1) 或者偷懒用字符串哈希做到 T(|S|)=O(log|S|),可以通过。

代码使用字符串哈希。里面有巨量注释及调试痕迹,谨慎阅读。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<ctime>
#include<random>
#include<cassert>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define all(x) x.begin(),x.end()
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) (x&-x)
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define mem(x,y) memset(x,y,sizeof x)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
using pii=pair<int,int>;
bool greating(int x,int y){return x>y;}
bool greatingll(long long x,long long y){return x>y;}
inline int rd(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-48;ch=getchar();}return x*f;
}
inline void write(int x,char ch='\0'){
	if(x<0){x=-x;putchar('-');}
	int y=0;char z[40];
	while(x||!y){z[y++]=x%10+48;x/=10;}
	while(y--)putchar(z[y]);if(ch!='\0')putchar(ch);
}
bool Mbg;
const int maxn=2e3+5,maxm=1e4+5,inf=0x3f3f3f3f;
const bool zgc_ak_ioi=true;
const int mod=787838027,base=59;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,k;
string s[maxn];
bitset<maxm>g[maxn];
string t[maxn];
//tv[i]:f[i-1][j]是否有值 
bool tv[maxm];
//(前缀长度,是否接上i)
pii sta[maxm];
int tp;
//f[i][j]的表示 
pii b[maxm];

int h[2][maxm];
int mi[maxm];
int hi[maxm];
int z[maxm];
//hi[i] LCP(str,str[i~end])
//z[i] LCP(str,txt[i~end])
inline int get(int l,int r){
	return (h[1][r]-(l?(1ll*h[1][l-1]*mi[r-l+1]%mod):0)+mod)%mod;
}
inline int __get__(int l,int r){
	return (h[0][r]-(l?(1ll*h[0][l-1]*mi[r-l+1]%mod):0)+mod)%mod;
}
void init(string str,string txt){
	int len1=str.size(),len2=txt.size();
	h[0][0]=str[0]-'a';
	rep(i,1,len1-1)h[0][i]=(1ll*h[0][i-1]*base%mod+str[i]-'a')%mod;
	h[1][0]=txt[0]-'a';
	rep(i,1,len2-1)h[1][i]=(1ll*h[1][i-1]*base%mod+txt[i]-'a')%mod;
	
	rep(i,0,len1-1){
		int l=0,r=len1-1-i,res=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(h[0][mid]==__get__(i,i+mid))res=mid+1,l=mid+1;
			else r=mid-1;
		}
		hi[i]=res;
	}
	rep(i,0,len2-1){
		int l=0,r=min(len1-1,len2-1-i),res=0;
		while(l<=r){
			int mid=(l+r)>>1;
			if(h[0][mid]==get(i,i+mid))res=mid+1,l=mid+1;
			else r=mid-1;
		}
		z[i]=res;
	}
}
//_ 下标
//0 y是x前缀 -1 y<x 1 y>x 
/*
case 1:
--------++++++
-----++++++
case 2:
-----------
----+++++
*/
int cmp(pii x,pii y,int _){
	int rev=1;
	if(x.fi<y.fi)swap(x,y),rev=-1;
	if(!y.se)return 0;
	int len=s[_].size();
	int __len__=y.fi+len;
	if(__len__<=x.fi){//case 2 
		if(z[y.fi]>=len)return 0;
		return s[_][z[y.fi]]<t[_-1][y.fi+z[y.fi]]?-rev:rev;
	}else{//case 1
		if(y.fi+z[y.fi]>=x.fi){
			if(!x.se)return 0;
			int num=x.fi-y.fi;
			if(num+hi[num]>=len)return 0;
			return s[_][num+hi[num]]<s[_][hi[num]]?-rev:rev;
		}else{
			return s[_][z[y.fi]]<t[_-1][y.fi+z[y.fi]]?-rev:rev;
		}
	}
}
inline void solve_the_problem(){
	cin>>n>>k;
	rep(i,1,n)cin>>s[i];
	mi[0]=1;rep(i,1,k)mi[i]=1ll*mi[i-1]*base%mod;
	g[n+1][0]=1;
	per(i,n,1)g[i]=g[i+1]|(g[i+1]<<s[i].size());
//	rep(i,1,n){
//		write(i,10);
//		rep(j,0,k)write(g[i][j],32);
//		P__;
//	}
	tv[0]=1;
	rep(_,1,n){
		init(s[_],t[_-1]);
		int len=s[_].size();
//		write(_,10);
//		rep(i,0,k)write(tv[i]);P__;
		rep(i,1,k)b[i]=mp(-1,-1);
		rep(i,1,k){
			if(tv[i])b[i]=mp(i,0);
			if(i>=len&&tv[i-len]){
				if(b[i]==mp(-1,-1)||(z[i-len]<len&&s[_][z[i-len]]<t[_-1][i-len+z[i-len]])){
					b[i]=mp(i-len,1);
				}
			}
		}
		tp=0;
		rep(i,1,k)if(b[i]!=mp(-1,-1)&&g[_+1][k-i]){
			while(zgc_ak_ioi){
				if(!tp){
					sta[++tp]=b[i];
					break;
				}
				int cp=cmp(b[i],sta[tp],_);
				if(!cp){
					sta[++tp]=b[i];
					break;
				}
				if(cp==-1)break;
				--tp;
			}
		}
//		write(_,10);
//		rep(i,1,tp)cerr<<t[_-1].substr(0,sta[i].fi)+(sta[i].se?s[_]:"")<<endl;
//		PU;
		t[_]=t[_-1].substr(0,sta[tp].fi)+(sta[tp].se?s[_]:"");
		tv[0]=1;rep(i,1,k)tv[i]=0;
		rep(i,1,tp)tv[sta[i].fi+sta[i].se*len]=1;
	}
//	rep(i,1,n)cerr<<t[i]<<endl;
	cout<<t[n];
}
bool Med;
signed main(){
//	freopen(".in","r",stdin);freopen(".out","w",stdout);
//	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=1;IOS;
	while(_--)solve_the_problem();
}
/*
5 10
m
mmm
nnnnn
mm
mmnnnnnnn
*/

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18444186

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示