题意 : 如果一个字符串包含两个相邻的重复子串,则称它是“容易的串”,其他串称为“困难的 串”。例如,BB、ABCDACABCAB、ABCDABCD都是容易的串,而D、DC、ABDAB、 CBABCBA都是困难的串。程序从输入中读取多行数据,每行包括两个整数n和L(即按此顺序给出),其中n > 0,L的范围是1 ≤ L ≤ 26。根据这些输入,程序要按照字母表升序打印出第n个“hard”字串(由字母表中的前L个字母构成),并在接下来的一行打印这个串的长度。按照上述规则,第一个串应该是“A”。对于给定的n和L,保证第n个“hard”串是一定存在的。比方说,当L = 3时,头7个“hard”字串为:
A
AB
ABA
ABAC
ABACA
ABACAB
ABACABA
分析 : 考虑使用深搜暴力一个个构造出合法的困难串,在深搜时按字典序考虑构造序列的每一位即可。但是有个难点,就是如何判断是否有重复?紫书给出了很好的解释=》“一种方法是检查所有长度为偶数的子串,分别判断每个字串的前一半是否等于后 一半。尽管是正确的,但这个方法做了很多无用功。还记得八皇后问题中是怎么判断合法性 的吗?判断当前皇后是否和前面的皇后冲突,但并不判断以前的皇后是否相互冲突——那些 皇后在以前已经判断过了。同样的道理,我们只需要判断当前串的后缀,而非所有子串。”换句话说就是在每判断一个位置的时候,我们只要枚举并检查含有新添加字母的偶数串合法后缀(也就是串的长度不要超过总长),就像书上说的,因为是一个个字母递增添加构造的,所以每一个都有和前面的进行判断,故只考虑当前而不考虑之前。
#include<bits/stdc++.h> using namespace std; int k, L; char * letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; int ans; bool check(char *temp, int last) { for(int j=1; 2*j<=last+1; j++){//->learn bool Equal = true; for(int k=0; k<j; k++){ if(temp[last-j+1+k] != temp[last-2*j+1+k]) {Equal = false;break;} //if(temp[last-k] != temp[last-k-j]) {Equal = false;break;}//也可以 } if(Equal) return false; } return true; } int cnt = 0; inline void DFS(int num, char *temp) { if(ans!=-1) return ; if(cnt==k) { temp[num]=‘\0‘;ans=strlen(temp);return ; } for(int i=0; i<L; i++){ if(ans!=-1) return ; temp[num] = letter[i]; if(check(temp, num)){ cnt++; DFS(num+1, temp); } } } int main(void) { // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); while(~scanf("%d %d", &k, &L) && (k&&L)){ ans = -1, cnt = 0; char temp[82]; DFS(0, temp); int blank = 0; int tot_group = 0; for(int i=0; i<ans; i++){ if(i != 0 && i % 64 == 0) printf("\n"); else if(i != 0 && i % 4 == 0) printf(" "); printf("%c", temp[i]); } printf("\n%d\n", ans); } return 0; }
瞎想 : 在考虑八皇后或此类深搜回溯进行构造的时候,如果需要有一些结合前面判断状态是否可行,那就多想想是否只考虑构成当前状态的新因素和已构造出的东西就可以判断状态是否可行,因为此类一个个递增构造的,每构造一个状态出来就要判别一次,当前状态的判别就不用理会之前的状态了,因为在做重复工作。