修仙录 3.21

今天杂题


jzoj 6071 不同的缩写

https://jzoj.net/senior/#main/show/6071
终于给我打出来了哈哈哈哈
序列自动机真是个好东西
判重不要用map(难打不说,还慢),trie真是个好东西
数组一定要开大,莫名WA等着你
在这里插入图片描述
good

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=305;
const int MAXM=MAXN*MAXN;

int n;
char name[MAXN][MAXN],st[MAXN];
int len[MAXN],sum;
int cnt,tot,head[MAXN],match[MAXM],ans[MAXN],isv[MAXM];
struct edge{
	int to,next;
	edge(){to=next=0;}
	edge(int to,int next):to(to),next(next){}
}g[MAXM];
struct node{
	string s;
	int to[26];
	node(){memset(to,0,sizeof(to));}
}p[MAXM];
struct sq_automaton{
	int next[MAXN][26];
	void build(int id){
		for(int i=len[id];i>=0;i--){
			for(int j=0;j<26;j++) if(!next[i][j]) next[i][j]=next[i+1][j];
			if(i) next[i-1][name[id][i-1]-'a']=i;
		}
	}
	void add(int u,int v){
		g[++cnt]=edge(v,head[u]),head[u]=cnt;
	}
	void find(int id,int dep,int x,int y,int lim){
		if(dep) add(id,y),++sum,p[y].s=string(st,st+dep);
		if(dep==lim||sum==n) return ;
		for(int i=0;i<26;i++){
			if(!next[x][i]) continue;
			st[dep]=i+'a';
			if(!p[y].to[i]) p[y].to[i]=++tot,p[tot]=node();
			find(id,dep+1,next[x][i],p[y].to[i],lim);
			if(sum==n) return ;
		}
	}
}sq[MAXN];

bool dfs(int x,int id){
	for(int i=head[x];i;i=g[i].next){
		int y=g[i].to;
		if(isv[y]==id) continue;
		isv[y]=id;
		if(!match[y]||dfs(match[y],id)){
			match[y]=x,ans[x]=y;
			return 1;
		} 
	}
	return 0;
}

bool check(int lim){
	cnt=tot=0,p[0]=node();
	memset(head,0,sizeof(head));
	for(int i=1;i<=n;i++) sum=0,sq[i].find(i,0,0,0,min(lim,len[i]));
	memset(isv,0,sizeof(isv)),memset(match,0,sizeof(match));
	for(int i=1;i<=n;i++) if(!dfs(i,i)) return 0;
	return 1;
}

int main(){
	freopen("diff.in","r",stdin);
	freopen("diff.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%s",name[i]);
		len[i]=strlen(name[i]);
		sq[i].build(i);
	}
	int l=1,r=300;
	while(l<r){
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	if(check(l)){
		cout<<l;
		for(int i=1;i<=n;i++) cout<<endl<<p[ans[i]].s;
	}
	else cout<<-1;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值