JZOJ-4644-NOI2016模拟7.16-人生的经验(欧拉回路+哈希)

题目描述(暂时找不到哪里可以提交)

题意:
定义人生经验为长度为l,字符集大小为c的所有字符串,求一个最短的包含所有人生经验的字符串。
我翻译一下:
要求一个最短的字符串,要包含所有能组合成的长度为 l 的字符串x,这些长度为 l 的字符串从一个长度 c 的字符串 y 中选字母(里面各字母都不相同且都是小写字母),x中字母可重复且不限,但得在y的范围内。


题目数据范围:
这里点一下。题目原文里写的是:“对于100%的数据:输出文件大小不超过10MB”
转换:
char的大小为1bytes,10MB=10485760bytes
所有答案的字符串最大范围 ==10485760,约等于10^7


假如不考虑重叠部分,每种字符串x独立,填充法可得,有c的 l 次方种,每种长度 l
如何构造可以使得长度最短,设想一种最优的方案。
每添加一个字符,都可以与当前已构造完毕的字符串的后l−1个构成一个新的长度为l的字符串,那么最终长度就是 c^l + l − 1。


我们将每个长度为l−1的字符串利用哈希,将它看成一个点,从一个字符串转换到另一个字符串只需要新增一个字符,在转换的过程中我们考虑用c进制数表示每个字符串,由于每个点都可以向外连出c条边,这样一来,每个点出度均为c;而每个点入度亦为c。根据判断条件,最终转化为求一条欧拉回路。

其实这里并不需要将所有点和边都建出来,将一个字符串压成一个数就可以了。


圈套圈算法求欧拉回路:
选一个点为起点,在图上乱走。假设走到死胡同了,此时在点a,往后退到点b,并存下走向死胡同的边。这条边是确定了方向的。再从b开始乱走,当又走回b时,相当于获得了一个更大的环

用一个栈记录每次走过的边,逆序输出。最后输出起点字符串。

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e7+7;
const int mod=1e9+7;
int read(){
    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-'0';
        ch=getchar();
    }
    return x*f;
}
int c,l,b[30],k,top;
int d[maxn],cnt[maxn],use[maxn],ans[maxn];
void cc(){
	d[top=1]=0;
	while(top){
		int x=d[top];
		if(cnt[x]<c){
			use[++top]=++cnt[x];
			d[top]=x%b[l-2]*c+cnt[x]-1;
		}
		else if(top>1)	ans[++k]=use[top--];
		else	break;
	}
}
string a; 
int main(){
	c=read();	l=read();
	cin>>a;
	b[0]=1;
	for(int i=1;i<=l;i++)	b[i]=b[i-1]*c;
	printf("%d\n",b[l]+l-1);
	if(l==1){
		cout<<a;
		return 0;
	}
	for(int i=1;i<l;i++)	putchar(a[0]);
	cc();
	for(int i=k;i;i--)	putchar(a[ans[i]-1]);
}

/*
2 2
ab

5
abbaa
*/
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值