UVa 129 Krypton Factor (DFS && 回溯)
題意 : 如果一個字符串包含兩個相鄰的重復子串,則稱它是“容易的串”,其他串稱為“困難的 串”。例如,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;
}
View Code
瞎想 : 在考慮八皇後或此類深搜回溯進行構造的時候,如果需要有一些結合前面判斷狀態是否可行,那就多想想是否只考慮構成當前狀態的新因素和已構造出的東西就可以判斷狀態是否可行,因為此類一個個遞增構造的,每構造一個狀態出來就要判別一次,當前狀態的判別就不用理會之前的狀態了,因為在做重復工作。
UVa 129 Krypton Factor (DFS && 回溯)