可重复排列
键盘输入一个仅由小写字母组成的字符串,输出以该串中任取M个字母的所有排列
及排列总数(输入数据均不需判错)。
此题是由全排列问题转变而来,不同之处在于:一个字符串中可能有相同的字符,导
致可能出现重复的排列。例如从字符串‘aab’中取2个字符的排列只有三种:‘aa’、‘ab’、
‘ba’。如何去掉那些可能重复的排列呢?一种想法就是每产生一种不同的排列就记录下
来,以便让以后产生的排列进行比较判重。这种想法显然没有考虑到随着字符串长度的增
加,排列将会多得无法记录,而且这种判重方法在效率上也会很低。最好有一种方法能在
产生排列的过程中就能将重复的去掉。先看一看全排列的递归过程:
PROCEDUREWork(k);
BEGIN
IFk=m十1
THEN打印结果
ELSEFORi←1TO字符串长度nDO
IFi<>e〔1〕,e[2〕…e[k-1]
THEN
BEGIN
e〔k〕←i;
Workk十1);
END;
END;
让我们来分析产生重复的原因。考虑从字符串‘aab’中取2个字符的排列。当e〔1]从
1变到2时,字符串中的字符却没有变,都是‘a’。这样我们只要在改变e〔k]时,判断其对
应的字符是否也改变。即在上面的过程的循环中加一句判断(设字符串为s):IFs[i]<>
s[e[k]]THEN…;这当然只是一个粗略的想法,我们仅仅用上面的例子就能发现问题:
程序在对e[k〕的每一次赋值之前都要进行一次判断,而不是我们预想的在改变e[k]时才
进行判重。我们用一个布尔型的局部变量First来记录是否是对e[k]进行第一次赋值。修
改后的程序如下:
PROCEDUREWork(k);
BEGIN
First←True;
IFk=m十1
THEN打印结果
ELSEFORi←1TO字符串长度nDO
IF(i<>e〔1〕,e〔2〕…e[k-1]AND
(FirstORs〔i[<>s〔e[k]])
THEN
BEGIN
e[k]←i;
First←False;
Work(k十1);
END;
END;
很多选手的程序到此就为止了,可是它还有一个致命的错误:我们在判重时假定这个
字符串中的字符已经排好顺序,即相同的字符已经连在一起。事实上并不是这样,输入的
字符串中的字符排列是任意的,需要我们在递归之前作一次排序的初始化才能保证程序
运行得正确。
键盘输入一个仅由小写字母组成的字符串,输出以该串中任取M个字母的所有排列
及排列总数(输入数据均不需判错)。
此题是由全排列问题转变而来,不同之处在于:一个字符串中可能有相同的字符,导
致可能出现重复的排列。例如从字符串‘aab’中取2个字符的排列只有三种:‘aa’、‘ab’、
‘ba’。如何去掉那些可能重复的排列呢?一种想法就是每产生一种不同的排列就记录下
来,以便让以后产生的排列进行比较判重。这种想法显然没有考虑到随着字符串长度的增
加,排列将会多得无法记录,而且这种判重方法在效率上也会很低。最好有一种方法能在
产生排列的过程中就能将重复的去掉。先看一看全排列的递归过程:
PROCEDUREWork(k);
BEGIN
IFk=m十1
THEN打印结果
ELSEFORi←1TO字符串长度nDO
IFi<>e〔1〕,e[2〕…e[k-1]
THEN
BEGIN
e〔k〕←i;
Workk十1);
END;
END;
让我们来分析产生重复的原因。考虑从字符串‘aab’中取2个字符的排列。当e〔1]从
1变到2时,字符串中的字符却没有变,都是‘a’。这样我们只要在改变e〔k]时,判断其对
应的字符是否也改变。即在上面的过程的循环中加一句判断(设字符串为s):IFs[i]<>
s[e[k]]THEN…;这当然只是一个粗略的想法,我们仅仅用上面的例子就能发现问题:
程序在对e[k〕的每一次赋值之前都要进行一次判断,而不是我们预想的在改变e[k]时才
进行判重。我们用一个布尔型的局部变量First来记录是否是对e[k]进行第一次赋值。修
改后的程序如下:
PROCEDUREWork(k);
BEGIN
First←True;
IFk=m十1
THEN打印结果
ELSEFORi←1TO字符串长度nDO
IF(i<>e〔1〕,e〔2〕…e[k-1]AND
(FirstORs〔i[<>s〔e[k]])
THEN
BEGIN
e[k]←i;
First←False;
Work(k十1);
END;
END;
很多选手的程序到此就为止了,可是它还有一个致命的错误:我们在判重时假定这个
字符串中的字符已经排好顺序,即相同的字符已经连在一起。事实上并不是这样,输入的
字符串中的字符排列是任意的,需要我们在递归之前作一次排序的初始化才能保证程序
运行得正确。