题目链接:https://vjudge.net/problem/UVA-129
题意:如果一个字符串包含两个相邻的重复子串,则称它为“容易的串”,否则称为“困难的串”,例如,BB,ABCDABCD,ABCDACABCAB,都是容易的串,而D,ABAC,都是困难的串。输入正整数n,L,让你找出由字母表前L个大写字母组成的字典序第n小的困难的串,并且输出这个串的长度。输出格式请点击上面题目链接参加原题。例如:L=3时,前7个困难的串为A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA。
分析:分析题意,貌似是一个全排列问题,可是需要我们生成所有由前L个大写字母组成的排列吗?那样计算量太大了。我们知道,全排列个数是一个阶乘级的数字,数字很容易爆炸。仔细想想可知,这道题可以用回溯法解决,可以减少很多没必要的判断。每次往答案后面添加一个字母,在这里,我们只需要判断包含这个字母的串是否合法,即只需要判断当前串的后缀即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=105;
int n,L;
int cnt,flag;
char ans[maxn];
void dfs(int cur) {
if(cnt==n) {// 如果找到了第n个串就输出答案并结束
flag=0;
for(int i=0; i<cur; i++) { //输出格式
if(i%64==0&&i) cout<<endl;
else if(i%4==0&&i) cout<<' ';
cout<<ans[i];
}
cout<<endl;
cout<<cur<<endl;
} else {
for(int i=0; i<L&&flag; i++) {
int ok=1;
ans[cur]=i+'A';//尝试把第个字母放入串中
for(int j=1; j*2<=cur+1&&ok; j++) { //判断是否合法
int is_equal=1;
for(int k=0; k<j; k++) {
if(ans[cur-k]!=ans[cur-k-j]) { //每个后缀中如果有一个字母不一样就可以判断当前后缀合法
is_equal=0;
break;
}
}
if(is_equal) {ok=0;break;}
}
if(ok) { //如果合法就继续寻找下一个字母
cnt++;
dfs(cur+1);
}
}
}
return;
}
int main () {
//freopen("in.txt", "r", stdin);
while(cin>>n>>L&&(n||L)) {
cnt=0;
flag=1;
dfs(0);
}
return 0;
}