作者:JF
题目描述
如果一个字符串包含两个相邻的重复子串,则称它为“容易的串”,其他的串称为“困难的串”。例如,BB、ABCDACABCAB、ABCDABCD都是容易地串,而D、DC、ABDAB、CBABCBA都是困难的串。
输入正整数n和L,输出由前L个字符组成的、字典序第k小的困难的串和该串的长度。例如,当L=3时,前7个困难的串分别为A、AB、ABA、ABAC、ABACA、ABACAB、ABACABA.以两个0最为结束输入的标志。由于这样的序列可能很长,请将其分成由四个字符组成的组,用空格分隔。如果有超过16个这样的团体,请为第17个团体开始一条新的路线。您的程序可能假设最大序列长度为80。
输入输出样例
输入
7 3
30 3
0 0
输出
ABAC ABA
7
ABAC ABCA CBAB CABA CABC ACBA CABA
28
思路
dfs从左到右依次枚举每个位置上的字符,然后判断枚举出来的串是否是困难的串,也就是判断串是否有相邻的相同子串,符合要求则继续往下枚举,否则回溯。
在判断串中是否有相邻重复子串时,因为之前找到的串已经判断过了,所以只需要判断新添加的字符所在的子串是不是重复子串就行,例如遍历到ABCDEFGH时,就只需要判断H和G、GH和EF、FGH和CDE、EFGH和ABCD是不是重复子串就行了。
注意点
1.题目所要求的是第n个串,而n个串不一定包含n个字符(一个位置可以放置不同的字符,只要他们是合法的)
2.基于‘1’,我们用cnt来记录这是第几个串,用x来保存串的长度。
3.注意输出的格式。
AC代码
#include<bits/stdc++.h>
using namespace std;
int n,L,cnt; //cnt记录这是第几个串
int a[100];
int dfs(int x)
{
if(cnt++==n){ //每次进入cnt+1并和n进行比较,相等则输出结果并返回0
for(int i=0;i<x;i++)
{
if(i%4==0&&i!=0&&i!=64) printf(" "); //每四个打印一个空格
printf("%c",a[i]+'A');
if((i+1)%64==0) printf("\n"); //有64个字符也就是16个团体就换行
}
if(cnt!=64) cout<<endl; //如果不是刚好一行,则换行
printf("%d\n",x); //输出字符串长度
return 0;
}
for(int i=0;i<L;i++) //依次枚举前L个字符
{
a[x]=i;
int ok=1; //定义ok标志判断i是否可行
for(int j=1;j*2<=x+1;j++) //枚举x/2次
{
int flag=0; //判断是否是重复子串
for(int k=0;k<j;k++)
{
if(a[x-k]!=a[x-j-k]){ //有一个字符不同则代表不是子串,结束本次循环
flag=1;
break;
}
}
if(!flag){ //flag==0则两子串相同 令ok=0后退出
ok=0;
break;
}
}
if(ok){ //i可行继续枚举下一个字符
if(dfs(x+1)==0) return 0; //找到退出
}
}
return 1;
}
int main()
{
while(cin>>n>>L&&n&&L) //读入n和L,并判断是否结束输入
{
cnt=0; //初始化cnt的值为0
dfs(0);
}
}