你在坐飞机的时候总是喜欢随便写点文字以打发时间。
对于一个单词S,如果存在一个长度L,满足0 < L< length(S),并且使得S长度为L的前缀与S长度为L的后缀相同,则称S是有界的。比如“aabaa”和“ababab”就都是有界的字符串。如果一个单词不存在这样的L,则称之为无界单词。
现在考虑所有仅由字母a和b组成的长度为N的字符串,你想知道:
1. 一共有多少个无界单词?
2. 这些无界单词中,按字典序排列第K小的单词是哪一个?
这是一道很神奇的题目。正常的直接用字符串的算法做不了的话那么就是字符串上DP了。
首先,第一个答案很好算,用f[i]表示长度为i的无界单词有多少个。
发现有界单词比较好求,所以可以用总数减去所有的有界单词就是无界单词的个数。首先我们可以知道一个结论就是用一个长度大于n\over 2 n2 的前缀然后再copy到后面成后缀所形成的有界单词中一定包含一个前缀小于n\over 2 n2 的前缀与相应的后缀相等,证明显然。所以在减去的所有有界单词中只用减到长度为 n2 的前缀就好了。
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=70,maxx=10007;
ll i,j,k,l,t,n,m,ans,len,u,v;
ll erci[maxn];
ll s[maxn];
ll next[maxn],f[maxn],h[maxn],g[maxn];
ll panduan(int qian,int zong){
ll hou=zong-qian+1;
return h[qian]-h[0]*g[qian]==h[zong]-h[hou-1]*g[zong-hou+1];
}
ll doing(){
ll i,j,k,l;
memset(f,0,sizeof(f));
fo(i,1,n){
if(iif(!next[i])f[i]=1;
continue;
}
f[i]=erci[i-len];
fo(j,1,i/2){
if(len<=j)l=erci[i-2*j];
if(len>j)l=erci[i-2*j-(len-j)];
if(len>i-j)l=panduan(len-(i-j),len);
f[i]-=f[j]*l;
}
}
return f[n];
}
void insert(ll x){
ll i,j;
s[++len]=x;h[len]=h[len-1]*maxx+x;
if(len==1)return;
while(u&&s[len]!=s[u+1])u=next[u];
if(s[len]==s[u+1])u++;
next[len]=u;
}
int main(){
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
scanf("%lld\n",&t);
erci[0]=g[0]=1;
fo(i,1,64)erci[i]=erci[i-1]*2,g[i]=g[i-1]*maxx;
while(t--){
scanf("%lld%lld",&n,&m);
len=0;
printf("%lld\n",doing());
fo(i,1,n){
v=u;
insert(0);
l=doing();
if(l1);
}
}
fo(i,1,n){
if(s[i])printf("b");else printf("a");
}
printf("\n");
}
}