算法竞赛入门经典 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 }

 

posted @ 2021-08-06 15:37  _vv123  阅读(122)  评论(0编辑  收藏  举报