关于字符串全排

编写字符串全排是一个练习递归和算法的好方式,同时也能见到数学知识与代码编写结合起来,求解问题会变得非常简单。这里摘录了两个字符串全排的例子,仅供参考。

例一,手机号码与字符全排: 键盘式的手机,对于9宫格式的手机,每个按键上都会刻有数字和字母,这样是否可以编写一个程序把数字转换成字母全排(相当于输入法....).

例二,对于给定的字符,写出其全排列。

两个例子比较经典,直接看代码吧。

// PhoneNumberToWords.cpp : Defines the entry point for the console application.
// Author: mingspy 5/22 2012 mingspy@163.com
//
// Solution of <<Beauty of Programming>> 3.2.
// Question:
//
// On old phone keyboard, the digital numbers and English alphas are paired, for
// example 2,A,B,C are in a single key, so can we design an algorithm to print 
// all permutation of a given phone number, e.g 1860000000000.

#include "stdafx.h"
#include <istream>

char code[10][10] = 
{
    "",             //0
    "",             //1
    "abc",          //2
    "def",          //3
    "ghi",          //4
    "jkl",          //5
    "mno",          //6
    "pqrs",         //7
    "tuv",          //8
    "wxyz",         //9
};

int codesLen[10]= {0, 0, 3, 3, 3, 3, 3, 4, 3, 4};

const int AnswerLen = 20;
int answer[AnswerLen] = {0};

// Translate phone number to words.
// The principle of this algorithm is using an array to save
// travel footprint, when the answer[i] equals codesLen[pNumber[i]]
// that means the permutation of this number has completed, then travel
// next until all number traveled.
/*长时间不看,回头来看自己写的代码,发现还是需要看一会才明白。所以应该写出代码流程或伪代码,然后再贴代码。
非递归手机号码与字符映射解决方法。
非递归的方法原理是:把全排看成数字递增。简单来说,比如手机有3个数字键[0,1,2] 对应9个字符[abc def ghi]那么
数字 0 -> abc      char * code[0]
数字 1 -> def      char * code[1]
数字 2 -> ghi      char * code[2]

那么对于每一个数字就有3个字符对应,那么打印出每个数字i对应的所有字符,就需要打印code[i][0-2]相当于1位三进制数
的累加然后转码。从0累加到2,并打印出数字i相应的第j个字符。
如果打印两个数字i,j对应字符的全排,那么相当于两个三进制数的累加转码问题从00到22。
用一个int数组表示当前的多进制数。
算法步骤:
初始化多进制数组为0
打印当前多进制数组代表的字符。
递增多进制数
如果多进制数越界,退出。

*/
void PhoneNumberToWords(int * pNumber, int nLen)
{
    // Check the in put parameters are reasonable.
    if(!pNumber)
    {
        return;
    }

    if(nLen > AnswerLen)
    {
        printf("Too many numbers.");
        return;
    }

    // Initialize answer to zero.
    for(int i = 0; i < nLen; i ++)
    {
        answer[i] = 0;
    }

    while(true)
    {
        // Print answer.
        for(int i = 0; i < nLen; i ++)
        {
            printf("%c", code[pNumber[i]][answer[i]]);
        }
        printf("\n");

        int k = nLen - 1;
        // Backwards increase answer[i] until all the character in this number key are
        // printed. It's like a trinary number increase step by step. 
        while(k>=0)
        {
            if(answer[k] < codesLen[pNumber[k]] - 1)
            {
                answer[k]++;
                break;
            }
            else
            {
                answer[k] = 0;
                k--;
            }
        }

        if(k < 0)
        {
            break;
        }
    }
}


// Print each words composition of the given number Recursively.
/*递归打印电话号码数字对应的字符全排。
对于全排问题,P(R1R2...RN) = P(R2...Rn)*P(R1)
算法步骤:
如果为一个号码排列,打印所有字符。
取出一个字符,打印其余字符的全排
*/
void RecursiveNumberToWords(int * pNumber, int nLen, int current)
{
    // Check the inputs, in order to improve the performance,
    // this lines could be comment out.
    if(!pNumber || nLen > AnswerLen )
    {
        return;
    }

    // Print the result.
    if(current == nLen)
    {
        for(int j = 0; j < nLen; j++)
        {
            printf("%c", code[pNumber[j]][answer[j]]);
        }
        printf("\n");
        return;
    }

    // if current number has no alpha company with,
    // print others. // 用于去除空字符,增加程序健壮性。
    if(codesLen[pNumber[current]] == 0)
    {
        RecursiveNumberToWords(pNumber, nLen, current+1);
    }
    else
    {
        // Recursively print each alpha.
        // 核心算法
        for(int i = 0; i < codesLen[pNumber[current]]; i++)
        {
            answer[current] = i;
            RecursiveNumberToWords(pNumber, nLen, current+1);
        }
    }  
}

// 一维字符全排问题
// Another Question about permutation:
// Please design a program to print all permutation of given characters.
void RecursivePermutationCharacters(char * pChars, int nLen, int iFrom)
{
    if(iFrom > nLen)
    {
        return;
    }

    // Assume Perm(CH) as the all permutation for CH={Ch(i)| i= 1, 2, ... n}, 
    // and set Perm(CHI)= CH - {Ch(i)};
    //according to mathematics knowledges, 
    //  Perm(n) = Ch(1)Perm(CH1) + Ch(2)Perm(CH2) + .... +Ch(n)Perm(CHn).
    if(iFrom < nLen)
    {
        for(int i = iFrom; i < nLen; i ++)
        {
            std::swap(pChars[i], pChars[iFrom]);
            RecursivePermutationCharacters(pChars, nLen, iFrom + 1 );
            std::swap(pChars[i], pChars[iFrom]);
        }
    }
    else
    {
        // print result.
        for(int i = 0; i< nLen; i++)
        {
            printf("%c", pChars[i]);
        }
        printf("\n");
    }
}

// Test case.
int _tmain(int argc, _TCHAR* argv[])
{
    int number[5];
    number[0] = 1;
    number[1] = 2;
    number[2] = 3;
    number[3] = 4;
    number[4] = 5;
    // Initialize answer={0}, as this is the first time 
    // use answer, don't have to do this.
    //RecursiveNumberToWords(number, 5, 0);

    char buf[3];
    buf[0] = '1';
    buf[1] = '2';
    buf[2] = '3';
    RecursivePermutationCharacters(buf, 3, 0);
    return 0;
}


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值