【HDU】4850 Wow! Such String! 提出猜想题 欧拉道路

本文探讨了一个有趣的字符串问题,即寻找长度为N、仅包含小写字母、且所有长度≥4的子串各不相同的有效字符串。通过两种方法详细分析了问题,并提供了可行的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自http://blog.csdn.net/u013368721/article/details/37575165


Wow! Such String!

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 112    Accepted Submission(s): 31
Special Judge


Problem Description
Recently, doge starts to get interested in a strange problem: whether there exists a string A following all the rules below:

1.The length of the string A is N .
2.The string A contains only lowercase English alphabet letters.
3.Each substring of A with length equal to or larger than 4 can appear in the string exactly once.

Doge cannot solve the problem, so he turns to his brother Yuege for help. However, Yuege is busy setting problems. Would you please help doge solve this problem?
 

Input
There are several test cases, please process till EOF.
For each test case, there will be one line containing one integer N (1 ≤ N ≤ 500000). 
Sum of all N will not exceed 5000000.
 

Output
For each case, please output one line consisting a valid string if such a string exists, or “Impossible” (without quotes) otherwise. You can output any string if there are multiple valid ones.
 

Sample Input
  
5 3 11 10 6 17 8
 

Sample Output
  
pwned wow suchproblem manystring soeasy muchlinearalgebra abcdabch
 

Source
2014西安全国邀请赛

传送门:【HDU】4850 Wow! Such String!

题目大意:给你一个整数n,让你输出一个字符串。必须满足以下条件:
1.字符串的长度为n。
2.这个字符串只包含小写字母。
3.字符串中长度大于等于4的子串最多只能出现一次。
如果无解输出Impossible。

题目分析:
这题我们就是提出猜想过掉的。。
因为大于等于4的子串只能出现一次,所以想到串最长只会达到26*26*26*26+3 = 456979的长度。
一开始是想到了AC自动机,想在AC自动机上每走三个结点就回到根。。可是这根本做不到= =于是又诞生了另一种奇葩的方法——每增加一个字母时从a~z for一遍,看这个字母会不会和前三个字母构成的子串在之前的串中已经出现,如果未出现过,那么这个位置上就决定是这个字母了,然后下个位置直接从接下去的字母中选取;如果已经出现相同子串则枚举下个字母,如果一直到z还是不能添加一个字母到本位置但是这次a~z中已经有至少一个添加到之前的位置上时,重头再for,否则结束循环(因为已经枚举完所有的26个字母且都已经与前面三个字母构成的子串出现过了)。一开始我们设的aaa作为起始串,然后输出最后得到的串的长度达到了456954!无限接近,然后输出还没有添加到串中的字母一看,竟然除了a,所有四个相同字母如bbbb,cccc等都没有添加进去。。。然后就机智的将它们全部放到前面去,再次输出一看,最长串长度正好456979!问题解决。(至此我也明白了四个字母的子串只出现一次的主串长度最长能达到(26 ^ 4) + 3 )

PS:可以大胆的猜想,可以有x个不同字符的长度大于等于m(m <= x)的子串最多只能出现一次的主串的最长长度可以达到(x ^ m) + m - 1!证明留待后人。

代码如下:
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std ;

#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define clear( a , x ) memset ( a , x , sizeof a )

typedef long long LL ;

const int MAXN = 26 ;
const int MAXS = 500005 ;

bool vis[MAXN][MAXN][MAXN][MAXN] ;
int s[MAXS] ;

int main () {
	int n , l = 0 , flag = 1 ;
	clear ( vis , 0 ) ;
	REP ( i , 0 , MAXN )
		s[l + 3] = s[l + 2] = s[l + 1] = s[l] = i , l += 4 ;
	REP ( i , 3 , l )
		vis[s[i]][s[i - 1]][s[i - 2]][s[i - 3]] = 1 ;
	while ( flag ) {
		flag = 0 ;
		REP ( i , 0 , MAXN )
			if ( !vis[i][s[l - 1]][s[l - 2]][s[l - 3]] ) {
				s[l] = i ;
				vis[s[l]][s[l - 1]][s[l - 2]][s[l - 3]] = 1 ;
				l ++ ;
				flag = 1 ;
			}
	}
	while ( ~scanf ( "%d" , &n ) ) {
		if( n > l )
			puts ( "Impossible" ) ;
		else {
			REP ( i , 0 , n )
				putchar ( s[i] + 97 ) ;
			putchar ( '\n' ) ;
		}
	}
	return 0 ;
}


转自http://blog.csdn.net/accelerator_/article/details/37579789

方法二:

思路:需要推理,考虑4个字母的字符串,一共有26^4种,这些由这些字符串,如果一个字符串末尾加上一个字符,可以变成另一个字符串的话,就当作这有一条边,每多一个字符多一个结点,那么对于这道题目,一共就能有26^4 + 3条边,在加上尾巴可以多放3个,一共是26^4+3个边,这些边全部连起来就是要的字符串,这样就可以知道每个节点会经过的次数为26,这样就只要考虑如何把这些节点串起来,形成一个欧拉道路即可,这样情况是最大的,选一个起始点aaa,不断往后走,每次选择被占用最少的节点去走,边都标记掉,然后要有一个注意点,就是由于是从aaa开始走,最后必须回到aaa,所以要让选择a这条边的优先级变成最小,不然如果先被占用了就无法构成了

#include <cstdio>
#include <cstring>

const int N = 20005;
int vis[N], vis2[N][30], on = 0;
char out[500005];

int getnext(int x, int a) {
    return x % (26 * 26) * 26 + a;
}

void init() {
    int now = 0;
    for (int i = 0; i < 3; i++)
	out[on++] = 'a';
    while (true) {
	int Min = 26, iv = 0;
	for (int i = 1; i < 26; i++) {
	    if (vis2[now][i]) continue;
	    int tmp = getnext(now, i);
	    if (vis[tmp] < Min) {
		Min = vis[tmp];
		iv = i;
	    }
	}
	int tmp = getnext(now, iv);
	if (vis[tmp] == 26) break;
	vis2[now][iv] = 1;
	now = tmp;
	vis[now]++;
	out[on++] = now % 26 + 'a';
    }
}

int n;

int main() {
    init();
    while (~scanf("%d", &n)) {
	if (n > 456979) printf("Impossible\n");
	else printf("%s\n", (out + 456979 - n));
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值