(题目描述略)
本题题意为求给定长度为 w 的数列的后五个 t-s 的组合计数(字典序,可能不存在)。
对于一个给定的数列 a[0 .. w-1],求其下一个字典序的 t-s 组合算法如下:
- 求出 t-s 组合的上界(末状态),记为 up 数组;
- 从右向左查询最大的下标 i (0 ≤ i ≤ w-1) 使得 a[i] < up[i];
- a[i] := a[i] + 1,从左向右 a[j] := a[j-1] + 1 (i+1 ≤ j ≤ w-1)。
算法分析:我们可以发现,第一步求出的 i 下标表示 a[i+1 .. w-1] 是一个长度为 w-i-1 的最后一个组合,且 a[i .. n-1] 是一个长度为 w-i 的非末组合。这样,我们可以不改变 a[0 .. i-1],而对 a[i .. n-1] 求其下一个组合。
因为以 a[i] 为起始的组合已经完成,所以其构造方法必然是将 a[i] 换成 a[i]+1,构造出以 a[i]+1 为起始的第一个组合。其方法与全排列有异曲同工之妙。
代码如下:
#include"iostream"
#include"stdio.h"
using namespace std;
char number[30],up[30];
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
int i,t,w;
scanf("%*d %d %d",&t,&w);
for(int i=0;i<w;i++)
cin>>number[i];
up[w-1]='a'+t-1;
for(int i=w-2;i>=0;i--)
up[i]=up[i+1]-1;
for(int step=0;step<5;step++)
{
for(i=w-1;(i>=0)&&(number[i]==up[i]);i--);
if(i<0)
break;
for(++number[i++];i<w;i++)
number[i]=number[i-1]+1;
for(int i=0;i<w;i++)
cout<<number[i];
printf("\n");
}
return 0;
}
时间复杂度:O(w)