紫书例题 Vijos P1189 困难的串

来源:互联网 发布:西安旅游人数数据 编辑:程序博客网 时间:2024/05/16 22:42

【问题描述】
 
  如果一个字符串包含两个相邻的重复子串,则称之为“容易的串”,其他串称为“困难的串”。例如:BB、ABCDACABCAB、ABCDABCD都是容易的,而D、DC、ABDAB、CBABCBA都是困难的。


  输入正整数 N 和 L,输出由前 L 个大写字母组成的、字典序第 N 小的困难串。例如,当 L=3 时,前7个困难串分别为:A、AB、ABA、ABAC、ABACA、ABACAB、ABCABA。输入保证答案不超过200个字符。
 
【输入格式】
 
  若干行,每行包含两个整数 N 和 L。
 
【输出格式】
 
  对应每个输入的 N 和 L ,输出符合要求的字符串;如果没有合适的解答,则输出"No answer."
 
【输入样例】
 
7 3
30 3


 
【输出样例】
 
ABACABA
ABACABCACBABCABACABCACBACABA


 
【数据范围】
 
  L <= 26。
  输入保证答案不超过200个字符。


分析:

嗯。。。看着这个题突然想起来一些回溯的实用剪枝技巧,总结如下:

比较性剪枝:一般回溯求一个最优值的时候,如果当前值已经比最优值差了,那么停止回溯。

预判性剪枝:如果当前我们得到一个值,与我们之后可以得到的解的最优情况相结合,如果不能比现在的最优解更优,那么就可以停止回溯。(典例:紫书“带宽”)

判断性剪枝:在得到一个解之后,我们有时还要验证是否可行,这时候我们可以减少不必要的判断。(本题就是一个很好的例子)

特性剪枝:对于一些题目,我们可以抓住该题的特性进行剪枝。(好吧实际上用起来就是上面那三个了)


好的回到这题,说是求字典序第N小的困难串,于是就在沿着字典序寻找困难串的时候加一个变量len标记现在是字典序第几大的困难串就可以了。

然后关注困难串的特性,是没有重复子串。那么假设现在我们已经得到了一个困难的串,那么我们只需要检查最后一个字母是否形成了以它结尾的重复子串就可以了(前面的串没有重复,现在加入一个字母,如果当前的串不是一个困难的串,那么重复的串中必然有这个字母参与)。

(PS:某瓜皮表示他想到了hash2333333可以可以)

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int N,L,s[205],len;void ans_out(int x) {for(int i=1;i<=x;i++) printf("%c",s[i]+'A');printf("\n");return;}bool get_bunch(int n) {if(len++==N){ans_out(n-1);return 1;}for(int i=0;i<L;i++){bool use=1;s[n]=i;for(int k=1;k*2<=len;k++){bool ok=0;for(int j=0;j<k;j++)if(s[n-k-j]!=s[n-j]){ok=1;break;} if(!ok){use=0;break;}}if(use) if(get_bunch(n+1)) return 1;}return 0; }int main(){freopen("test.in","r",stdin);freopen("test.out","w",stdout);while(scanf("%d%d",&N,&L)==2){if(!get_bunch(1)) printf("No answer.\n");len=0;}return 0;}


原创粉丝点击