【题目描述】给定k,你需要找到一个最小的n,使得有至少一个1~n的排列不是任何一个字符集不超过k的字符串的后缀排名数组,并构造出这样的排列中字典序最小的一个。 后缀排名数组指:对于一个字符串,将其所有后缀按字典序排序,第i的后缀排名为a[i]。如abbab的后缀排名数组为 2 5 4 1 3。 【输入】一行一个正整数k。 【输出】输出包含两行。第一行为n,第二行为一个1~n的排列满足题目条件。 【输入样例】2 【输出样例】3 2 1 3 【提示】长为2的字符集不超过2的字符串有aa,ab,ba。后缀排名数组分别为2 1,1 2,2 1。包含了所有的排列。长为3的字符集不超过2的字符串有aaa,aab,aba,abb,baa,bab,bba。后缀排名数组分别为3 2 1,1 2 3,2 3 1,1 3 2,3 2 1,3 1 2,3 2 1。因此2 1 3是符合条件的解。 对于20%的数据点,1<=k<=10; 对于50%的数据点,1<=k<=1000; 对于100%的数据点,1<=k<=100000。 |
发现当n <= k的时候一定存在合法序列,考虑n = k + 1
我们要使每个位置的字母都不相同,这样即不合法。
即对于位置 a 和 b,a + 1 和 b + 1 与 a 和 b 之间的大小关系应该相反。
否则就可以令 a 和 b 为同一字符。
然后考虑n,显然任何数都比它小,所以它后面应该是最小的:0,所以放最后。
考虑1,显然任何数都比它大,所以它后面应该是最大的:n
然后交替倒推即可构造。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 const int N = 100010; 3 4 int a[N]; 5 6 int main() { 7 int n; 8 scanf("%d", &n); 9 n++; 10 printf("%d ", n); 11 int t = n + 1; 12 for(int i = n; i > 0; i -= 2) { 13 a[i] = --t; 14 } 15 for(int i = a[1] ? 2 : 1; i <= n; i += 2) { 16 a[i] = --t; 17 } 18 for(int i = 1; i <= n; i++) { 19 printf("%d ", a[i]); 20 } 21 return 0; 22 }
关于字典序最小,据说可以证明这样构造出来的是 n = k + 1 时的唯一解。不会。