题目大意
一个长度为n,只含有
要求解答两个问题:
∙共有多少个长度为n的无界单词
一个数据点共有T个测试数据。
题目分析
首先我们可以求出有界单词,然后用总数减一减得到无界单词。
为了避免重复计算,我们考虑如何表达一个有界单词?可以发现一个有界单词一定为前缀和后缀为某一无界单词,中间填上一些其它字符,而且这个无界单词是唯一的,且长度不会超过原串一半。
那么现在第一问的解决就显然了。
为了适应c++的数组下标,一下递推方程采用从0编号的顺序。
令
显然可得
fi=2i+1−∑j=02(j+1)≤i+1fj×2i−2j−1
现在考虑如何求解第二个询问,考虑我们目前已经得到长度为len的串,那么我们要计算采用这种串作为前缀的无界单词个数。
令gi表示枚举到第i位(长度为
- i<len
直接判断已知串前i+1位是否是无界单词即可
否则依然使用2i+1−len减去有界单词个数,枚举j表示考虑前缀
- len×2≤i+1
- j<len:该位对fi的贡献为fj×2i−2j−1
- j≤len :该位对fi的贡献为fj×2i−j−len
- len×2>i+1
- i−j<len:该位对fi的贡献为fj×2i−j−len
- i−j≤len:此时我们选取的部分作为后缀的时候会和已知串的后面某些位置重叠,所以我们要使用哈希、KMP预处理或者暴力等方法判断一下子串[0,len−i+j)与子串[i−j,len)是否相同,相同则贡献为1。
那么我们直接递归每一位,先填上
时间复杂度为
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N=65;
LL f[N],pw[N];
char c[2]={'a','b'};
bool s[N];
int n,T;
int p[N];
LL K;
void pre()
{
pw[0]=1;
for (int i=1;i<N-1;i++)
pw[i]=pw[i-1]*2;
}
void clear()
{
for (int i=0;i<n;i++)
f[i]=0;
}
void calcall()
{
clear();
f[0]=2;
for (int i=1;i<n;i++)
{
for (int j=0;(j+1)*2<=i+1;j++)
f[i]+=f[j]*pw[i-j*2-1];
f[i]=pw[i+1]-f[i];
}
}
void kmp(int len)
{
p[0]=0;
int cur=0;
for (int i=1;i<len;i++)
{
while (cur&&s[cur]!=s[i])
cur=p[cur-1];
if (s[cur]==s[i])
cur++;
p[i]=cur;
}
}
bool same(int st0,int en0,int st1)
{
for (int i=0;st0+i<=en0;i++)
if (s[i+st0]!=s[i+st1])
return false;
return true;
}
LL calc(int len)
{
clear();
kmp(len);
for (int i=0;i<n;i++)
if (i<len)
f[i]=p[i]==0;
else
{
for (int j=0;(j+1)*2<=i+1;j++)
if (len*2<=i+1&&j>=len)
f[i]+=f[j]*pw[i-j*2-1];
else
if (len<=i-j)
f[i]+=f[j]*pw[i-j-len];
else
if (same(i-j,len-1,0))
f[i]+=f[j];
f[i]=pw[i+1-len]-f[i];
}
return f[n-1];
}
void find(int cur,LL rank)
{
if (cur==n)
return;
s[cur]=0;
LL sum=calc(cur+1);
if (sum>=rank)
{
find(cur+1,rank);
return;
}
s[cur]=1;
find(cur+1,rank-sum);
}
int main()
{
pre();
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
scanf("%d",&T);
while (T--)
{
scanf("%d %lld",&n,&K);
calcall();
printf("%lld\n",f[n-1]);
find(0,K);
for (int i=0;i<n;i++)
putchar(c[s[i]]);
putchar('\n');
}
fclose(stdin);
fclose(stdout);
return 0;
}