//有时候遇上往后缀添加任何字母都不合适的情况
//递归迭代的实质是从一个点调到另外一个点,所以需要先输入一个点
//放心,递归遍历所有字串一定是按照字典序排列的,你所关心的已经改是怎样剪枝回溯的问题
//编程细节只有ac之后你才会发现,所以对一个题,一定亲自ac掉。
//Uva 129困难的串
#include<iostream>
#include<cstdio>
using namespace std;
int a,b,cnt;
int cur1=0;
int shz[85];
//递归迭代的结构
//初始结构
//结束结构
//枚举子节点作为根节点的结构
int jud(int cur){//从后面判断相邻子串方法 i=1;i*2<=cur+1
int ok=0;
for(int i=1;i*2<=cur+1;i++){
int equals=0;
for(int j=0;j<i;j++){
if(shz[cur-j]!=shz[cur-i-j]){
equals=1;
break;
}
}
if(!equals){
ok=1;
break;
}
}
// cout<<ok<<" "<<cur<<" test OK"<<endl;
if(ok)return 0;
return 1;
}
void out(int cur){//输出格式注意
for(int i=0;i<cur;i++){
if(i%4==0&&i>0){//0时不操作
if(i%64==0)printf("\n");
else printf(" ");
}
printf("%c",'A'+shz[i]);
}
cout<<endl;
cout<<cur<<endl;
}
//递归迭代剪枝的方法
//ds返回的值为 0 或者1,0表示该分支上有解,1表示该枝上无解需要调到另外一棵枝上。所以调用ds时候要从初始点开始枚举
//递归枚举的条件判断结构可以在本枝上剪枝,有两种状态,一是无解深搜为1,二是有解深搜到1,前一个会遍历整个分支,后者找到解之后就不管了,相当于剪掉后面的枝叶
//从初始点开始枚举调用ds的意义在于,对于一个没有解返回为1的支,会接着调到另外一个枝干上找解
//递归迭代做条件的意义在于剪枝,因为递归迭代会枚举所有的可能性,但是我们只需要枚举到我们需要的部分就行,许哦一需要递归迭代的条件结构完成剪掉后面的枝条的作用
int ds(int cur){//在选中的枝干中找解
cur1=cur;
if(cnt==a){
out(cur);
return 0;
}
else{
for(int i=0;i<b;i++){
shz[cur]=i;
if(jud(cur)){
// cout<<cur<<" "<<i<<" test"<<endl;
cnt++;
if(!ds(cur+1))
return 0;
}
}
}
return 1;
}
int main(){
while(cin>>a>>b&&a){
cnt=0;
cur1=0;
//选择枝干:解答树的起点即为根节点,选择了起点就是选择了一个枝干 递归迭代按照解答树从上到下从左往右字典序遍历
//注意全局cur1的使用是为了保存从上到下从左到右的递归连续性,保存上一个节点排列的位置
//break在找到解之后结束查找
for(int i=0;i<b;i++){
if(!ds(cur1))break;}
}
return 0;
}
//总之:
//1.递归迭代按照解答树从上到下从左往右字典序遍历
//2.递归迭代做条件会在选择的枝干上迭代//构造方法是if(!ds(cur+1)) return 0; return 1;// 调用方法是while(1) 直到找到解//最终返回的是1表示遍历该枝干上无解,返回0表示有解并且不再迭代作为剪枝
//3.递归迭代可以选择特定的迭代枝干,方法是设置相应的cur调用ds(cur) 比如这道题遍历以B为初始节点的排列只需要设置shz[0]='B',cur=1;然后ds(cur)
//4.递归迭代实质是从一个节点跳到另外一个节点,一般树的dfs方法是引入处理根节点,空树条件,遍历子节点作为根结点;解答树的ds方法是引入一个数组下标位置cur,结束条件if(cur),else尝试判断规定元素b并进入下一个位置ds(cur+1)
//5.子集生成,排列生成,回溯,解答树都是递归迭代问题