解法
这个题我看解答都想了好久= =
对于小数据集,当N<=100
时,可以通过计算出以S
为前缀的字符串有多少个来慢慢遍历,记作PN(S)P_N(S)PN(S)
为了方便大数据集的分析,我们把空串也算上,空串是K=0
对应的串
处理过程伪代码如下:
def solve(l,N,K):
prefix = ""
while True:
if K < P(prefix+"$"): # 是否可以直接成回文串,如果可以P是1,不可以P是0
return len(prefix)
K -= P(prefix+"$")
for i in range(l):
c = 'a'+i
if K<P(prefix+c):
prefix += c
break
else:
prefix -= P(prefix+c)
else: # 如果所有可用字符都遍历完了也不确定下一个字符是啥,那就说明K太大了
return 0
PN(S)P_N(S)PN(S)的求法如下:
PN(S)=∑l=len(S)NP(S,L)P_N(S) = \sum_{l = len(S)}^NP(S,L)PN(S)=∑l=len(S)NP(S,L),其中,P(S,L)P(S,L)P(S,L)表示以S
开头的,长度为L
的回文串
P(S,L)
的求法如下:
- 如果
S
的长度超过L
的一半,那么要检查超过部分是否是回文,如果是,P(S,L)=1
,否则为0 - 如果
S
的长度不足L
的一半,len(S)<⌊L+12⌋len(S)<\lfloor\frac{L+1}{2}\rfloorlen(S)<⌊2L+1⌋,不足一半的那部分可以随便变换,所以可能性有l⌊L+12⌋−len(S)l^{\lfloor\frac{L+1}{2}\rfloor-len(S)}l⌊2L+1⌋−len(S)种
现在如果N
可能很大,我们不能一个一个字符推测了,而是应该根据K
的大小来直接得到一个前缀:
-
如果
K<=N
,首先我们知道,这个回文串序列的前N
个一定是长度分别为1,2,...,N
的全是a
的串,所以此时第K
个串对应的长度一定是K
-
如果
K>N
:
首先,以t
个a
为前缀的回文串,我们记作AtA_tAt,并且有PN(At)=∑L=t2t−1P(At,L)+∑L=2tNP(At,L)P_N(A_t) = \sum_{L=t}^{2t-1}P(A_t,L)+\sum_{L=2t}^{N}P(A_t,L)PN(At)=L=t∑2t−1P(At,L)+L=2t∑NP(At,L),前一部分很明显满足条件的只能是全a
的串,所以等于t
,并且它们刚好是以AtA_tAt开头的前t
个回文串;而后一部分全部都是长度为L的以AtA_tAt开头和结尾的回文串,它等价于长度为L-2t
的任意回文串,因此最后我们有:PN(At)=t+∑L=2tNP("",L−2t)=t+∑L=0N−2tP("",L)=t+PN−2t("")P_N(A_t) = t+\sum_{L=2t}^{N}P("",L-2t)\\=t+\sum_{L=0}^{N-2t}P("",L)=t+P_{N-2t}("")PN(At)=t+L=2t∑NP("",L−2t)=t+L=0∑N−2tP("",L)=t+PN−2t("")而PN−2t("")P_{N-2t}("")PN−2t("")恰好对应于一个更小规模(N′=N−2tN'=N-2tN′=N−2t)的问题,也就是说,如果我们能确定
K
对应的回文串开头有多少个a
,那么问题的规模就能减小现在,按照我们的遍历逻辑:
1. 前缀为"a"
时,如果K==1
,那么对应的串是"a"
(当然这在K>N
时不会发生)
如果K>=2
且K-2<P("aa")
,那么K
对应的串将以"aa"
开头,而由于P("aa")
很大,这是很有可能的
而如果K>=2
且K−2<PN−2("")K-2<P_{N-2}("")K−2<PN−2(""),那么K
对应的回文串就落在了P("a")
的第二部分,它将是以"a"
开头,"a"
结尾的串,并且是长为N-2
的字典序回文串里的第K-2
个。
2. 接下来,可能由于K>=3
并且K-3<P("aaa")
,所以对应的串将以"aaa"
开头,由于K>=4
且K−4<PN−4("")K-4<P_{N-4}("")K−4<PN−4(""),它将是以"aa"
开头,"aa"
结尾的串,并且是长为N-4
的字典序回文串里的第K-4
个。以此类推,我们有:
假如
K>=t
(这条由于K>N
一定成立)且K−t<P(At)K-t< P(A_t)K−t<P(At),那么对应的串将由AtA_tAt开头
假如K>=2t
且K−2t<PN−2t("")K-2t< P_{N-2t}("")K−2t<PN−2t(""),那么对应的串将由AtA_tAt开头,AtA_tAt结尾,并且是长为N-2t
的字典序回文串里的第K-2t
个。现在我们想确定一个尽量大的
t
,使得K
对应的串以AtA_tAt开头,AtA_tAt结尾,并且是是长为N-2t
的字典序回文串里的第K-2t
个.根据PN−2t("")P_{N-2t}("")PN−2t("")的定义我们知道PN−2t("")≥P("",N−2t)=l⌊N+12⌋−tP_{N-2t}("")\ge P("",N-2t)=l^{\lfloor\frac{N+1}{2}\rfloor-t}PN−2t("")≥P("",N−2t)=l⌊2N+1⌋−t,当且仅当
2t==N
取等号
而K−2t≤KK-2t\le KK−2t≤K,当且仅当t==0
时取等号
由于N>0
,所以它们不会同时取等号,这意味着如果我们让K≤l⌊N+12⌋−tK\le l^{\lfloor\frac{N+1}{2}\rfloor-t}K≤l⌊2N+1⌋−t,就能让K−2t<PN−2t("")K-2t<P_{N-2t}("")K−2t<PN−2t("")。
解得t=⌊⌊N+12⌋−loglK⌋t=\lfloor\lfloor\frac{N+1}{2}\rfloor-\log_lK\rfloort=⌊⌊2N+1⌋−loglK⌋
如果t<0
,说明K
太大了,应该返回0。
然后问题就从solve(l,n,k)
变成2t+solve(l,n-2t,k-2t)
啦,由于k∈(232,264)k\in(2^{32},2^{64})k∈(232,264),所以loglK\log_lKloglK不会超过64,而n-2t
接近2loglK2\log_lK2loglK,所以问题规模不会比小数据集大很多。
# -*- coding:utf-8 -*-
import math
INF = 10**5
BASE = ord('a')
END = '$'
def isPalindrome(s,l,r):
while l<r:
if s[l]!=s[r]:
return False
l += 1
r -= 1
return True
def solve(l,n,k):
def P(s,l,n):
d = len(s)
if s[-1]=='$':
return 1 if isPalindrome(s,0,d-2) else 0
res = 0
for L in range(d,n+1):
diff = (L+1)//2-d
if diff>=0 or isPalindrome(s,L-d,d-1):
res += l**max(0,diff)
return res
if k<=n:
return k
if l==1:
return 0
t = int((n+1)//2-math.log(k,l))
if t<0:
return 0
n -= (t<<1)
k -= (t<<1)
prefix = ""
while True:
res = P(prefix+'$',l,n)
if res<=k:
k -= res
else:
return len(prefix)+(t<<1)
for i in range(l):
c = chr(BASE + i)
res = P(prefix+c,l,n)
if res<=k:
k -= res
else:
prefix += c
break
else:
return 0
if __name__ == "__main__" :
t = int(input())
for round in range(1,t+1):
l,n,k = map(int,input().split())
print("Case #%d: %d"%(round,solve(l,n,k)))