算法竞赛入门经典 7-5 困难的串 UVa129(回溯法)
题意:将一个包含两个相邻的重复子串的字符串,称为“容易的串”,其他为“困难的串”。 输入正整数n和l,输出由前l个大写字母组成的,字典序第n小的困难的串。
提交地址:UVA129 困难的串 Krypton Factor - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
一种容易想到的做法是每次在末尾添加字符,然后枚举所有长度为偶数的子串判断是否合法。
显然,这样做会造成大量的重复计算。对于一个新增的字符,我们只需要考虑以它为末尾的子串,也即当前字符串的后缀,这样即可不重不漏地遍历所有枚举到的字符串。 当一个串成为“简单的串”后,往后再添加字符也不会成为困难的串,此时回溯即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 int n, l, cnt; 5 string str; 6 7 void print(string str) { 8 int len = str.length(); 9 for (int i = 0; i < len; i++) { 10 printf("%c", str[i]); 11 if (i && (i + 1) % 64 == 0) printf("\n"); 12 else if (i && (i + 1) % 4 == 0 && i != len - 1) printf(" "); 13 } 14 if (len % 64) printf("\n"); 15 printf("%d\n", len); 16 } 17 18 bool check(string str) { 19 int len = str.length(); 20 for (int k = 1; k * 2 <= len; k++) { 21 string s1 = str.substr(len - k, k); 22 string s2 = str.substr(len - k * 2, k); 23 if (s1 == s2) return true; 24 } 25 return false; 26 } 27 28 bool dfs(int len) { 29 //getchar(); 30 if (!check(str)) cnt++; 31 else return false; 32 if (cnt == n) { 33 print(str); 34 return true; 35 } 36 for (char c = 'A'; c - 'A' < l; c++) { 37 str.push_back(c); 38 if (!dfs(len+1)) str.pop_back(); 39 else return true; 40 } 41 return false; 42 } 43 44 int main() { 45 while (cin >> n >> l && n) { 46 str = ""; 47 cnt = 0; 48 for (char c = 'A'; c - 'A' < l; c++) { 49 str.push_back(c); 50 if (!dfs(1)) str.pop_back(); 51 else break; 52 } 53 } 54 return 0; 55 }