数据结构与算法案例集-002-算法基础

来源:互联网 发布:卖牛仔裤的淘宝店铺 编辑:程序博客网 时间:2024/05/01 14:27
 

案例2-1 求解指定实数的正的平方近似根

目标

通过求解二次方程的近似实数根学习迭代算法的一般设计思路。

源文件:calculate_square_root.cpp

#include <stdio.h>

 

//

//calculate_square_root:

//        使用迭代算法计算指定实数的平方近似根;

//参数:

//   pdOutR:         [in,out]保存计算的平方近似根;

//   dInR:             [in]需要计算平方根的正实数;

//   dE:               [in]计算精度;

//   nTime:           [in]迭代最大次数(-1时不检查迭代次数);

//返回:

//   如果超过迭代次数,则返回-1;

//   如果找到精确根,则返回1;

//   如果找到近似根,则返回0;

//

int  calculate_square_root(double* pdOutR, double dInR, double dE,

     unsigned long nTime)

{

     double            dX0 = 0.0;

     double            dX1 = 0.0;

     double            dMax = 0.0;

     double            dMin = 0.0;

     double            dPitch = 1.0;

     double            dVal = 0.0;

     double            dAbs = 0.0;

     unsigned long  nAcc = 0;

     int           nRlt = 0;

 

     dX0 = 0.0;

     dX1 = 0.0;

     dMin = 0.0;

     dPitch = 1.0;

     nAcc = 0;

     while(1){

            //检查迭代次数;

            nAcc ++;

            if((nTime!=-1) && (nAcc>nTime))

            {

                   nRlt = -1;

                   break;

            }

            dX1 = dX0 + dPitch;

            dVal = dX1 * dX1 - dInR;

            if(dVal == 0)

            {

                   //相等则得到精确根;

                   nRlt = 1;

                   break;

            }

            else if(dVal < 0)

            {

                   dX0 = dX1;

                   dMin = dX1;

            }

            else  {

                   dMin = dX0;

                   dMax = dX1;

                   dPitch = (dMax-dMin)/2;

                   //检查计算精度;

                   dAbs = (dX0>=dX1)?(dX0-dX1):(dX1-dX0);

                   if(dAbs < dE)

                   {

                          nRlt = 0;

                          break;

                   }

            }

     }

     *pdOutR = dX1;

 

     return      nRlt;

}

 

void main(void)

{

     double            dOutR = 0.0;

     double            dInR = 35221.93234;

     double            dE = 0.00000001;

     unsigned long  nAcc = -1;

 

     calculate_square_root(&dOutR, dInR, dE, nAcc);

}

迭代法是一种以反复、循环、重述(或重复)为特征的解决问题的方法。计算一个实数的平方近似根需要指定一个精度(或迭代最大次数),在指定的限制条件下,通过上面的迭代方法可以获取得需要的平方近似根。

案例2-2 查找指定字符串的子串的位置

目标

学习穷举搜索法的设计思路。

源文件:find_substring.cpp

#include <stdio.h>

#include <string.h>

 

//

//find_substring:

//        从指定字串中查找指定的子串;

//参数:

//   pSrc:       [in]目标字符串;

//   pSub:      [in]子字符串;

//返回:

//   子串的位置(NULL);

//

char* find_substring(const char *pSrc, const char *pSub)

{

     char*      pRlt = NULL;

     char*      ps = NULL;

     int    nSrcLen = 0;

     int    nSubLen = 0;

     int    acc = 0;

     int    nEqu = 0;

 

     //检查输入参数;

     if(!pSrc || !pSub) return NULL;

 

     //获取并比较字串的长度;

     nSrcLen = strlen(pSrc);

     nSubLen = strlen(pSub);

     if(nSrcLen < nSubLen)

     {

            return      NULL;

     }

 

     //依次对比字串;

     for(ps=(char*)pSrc,acc=0; acc<=(nSrcLen-nSubLen); ps++,acc++)

     {

            nEqu = memcmp(ps, pSub, nSubLen);

            if(nEqu == 0)

            {

                   pRlt = ps;

                   break;

            }

     }

     return      pRlt;

}

 

void main(void)

{

     char pSrc[] = "Hello,nice to meet you!";

     char pSub1[] = "to ";

     char pSub2[] = " you ";

     char*      pPos = NULL;

 

     pPos = find_substring(pSrc, pSub1);

     if(pPos != NULL)

     {

            printf("Found substring(%s) from (%s)./r/n", pSub1, pSrc);

     }

     else  {

            printf("Not Found substring(%s) from (%s)./r/n", pSub1, pSrc);

     }

 

     pPos = find_substring(pSrc, pSub2);

     if(pPos != NULL)

     {

            printf("Found substring(%s) from (%s)./r/n", pSub2, pSrc);

     }

     else  {

            printf("Not Found substring(%s) from (%s)./r/n", pSub2, pSrc);

     }

}

穷举搜索法是对可能的结果按某种顺序进行逐一枚举和检验,并从中找到那些符合要求的结果。使用穷举搜索法的重要的前题是可能的结果是有限个并且可以枚举。穷举搜索法的算法复杂度低,但计算量可能很大。

案例2-3 N!

目标:

求整数N的阶乘学习递推法。

源代码:factorial.cpp

#include <stdio.h>

#include <stdlib.h>

 

#define MAX_ARRAY_DIGIT           200

#define MAX_N                 100

 

int  g_pnVal[MAX_ARRAY_DIGIT];

 

int main(int argc, char* argv[])

{

     int    nFTL = 0;

     int    nDigit = 0;

     int    nVal = 0;

     int    nP = 0;

     int    i = 0;

     int    j = 0;

 

     //检查参数;

     if(argc < 2)

     {

            printf("Usage: factorial N(2<=N<=100)/r/n");

            return -1;

     }

     nFTL = atol(argv[1]);

     if((nFTL<2) || (nFTL>MAX_N))

     {

            printf("Usage: factorial N(2<=N<=100)/r/n");

     }

    

     //递推计算和显示N的阶乘(已知(i-1)!,i!);

     g_pnVal[0] = 1;

     nDigit = 1;

     for(i=2; i<=nFTL; i++)

     {

            //i依次乘(i-1)!的各位(可能需要进位);

            for(nP=0,j=0; j<nDigit; j++)

            {

                   nVal = g_pnVal[j] * i + nP;

                   g_pnVal[j] = nVal%10;

                   nP = nVal/10;

            }

            //处理最后的进位;

            while(nP)

            {

                   g_pnVal[nDigit++] = nP%10;

                   nP /= 10;

            }

            //显示计算的结果;

            printf("%02d!= ", i);

            for(j=nDigit-1; j>=0; j--)

            {

                   printf("%d", g_pnVal[j]);

            }

            printf("/r/n");

            //检查是否超界;

            if(nDigit >= MAX_ARRAY_DIGIT-1)

            {

                   printf("overlay the array, exited!/r/n");

                   break;

            }

     }

     return      0;

}

递推法是利用问题本身所具有的一种递推关系解决问题的一种方法。使用递推法求整数N的阶乘(N!=N*(N-1)*(N -2)*…*3*2*1)。N的阶乘有一个明显的递推关系:N!=N*(N-1)!。依次求出1!2!3!(N-1) !,则可以得到N!

计算机的整数位数是有限的,N!的结果很可能超过最大的整数(如128位),所以算法设计中,使用数组来存储大整数i!。为了方便,设数组的每个元素存放大整数的一位数字,并将整数i!自个位到高位顺序存储于数组中。

案例2-4 找出从自然数1N中任取R个数的所有组合

目标

学习递归算法和回溯算法。

源代码:comb_integer.cpp

#include <stdio.h>

#include <stdlib.h>

 

#define MAX_N          100

 

int  g_pnA[MAX_N];

int  g_nR = 0;

 

int comb_integer(int nM, int nK)

{

     int    i = 0;

     int    j = 0;

 

     //递归方法计算组合问题;

     for(i=nM; i>=nK; i--)

     {

            g_pnA[nK-1] = i;

            if(nK > 1)

            {

                   //组合中还有其它元素;

                   comb_integer(i-1, nK-1);

            }

            else  {

                   //输出获取的给合;

                   for(j=g_nR-1; j>=0; j--)

                   {

                          printf("%4d", g_pnA[j]);

                   }

                   printf("/r/n");

            }

     }

     return      0;

}

 

int main(int argc, char* argv[])

{

     int    nM = 0;

     int    nK = 0;

 

     //检查参数;

     if(argc < 3)

     {

            printf("Usage: comb_integer N(2<N<100) K(K<=N)/r/n");

            return -1;

     }

     nM = atol(argv[1]);

     nK = atol(argv[2]);

     if((nM<2) || (nM>MAX_N) || (nK<1) || (nK>nM))

     {

            printf("Usage: comb_integer N(2<=N<=100) K(K<=N)/r/n");

     }

     g_nR = nK;

    

     //递归计算组合;

     comb_integer(nM, nK);

     return      0;

}

当组合的第一个数据选定时,其后的数据是从余下的m-1个数中取k-1个数的组合。从而将求m个数中取k个数的组合问题,转化成求m-1个数中取k-1个数的组合问题。

函数引入工作数据a[]存放求出的组合的数字,约定函数将确定的k.个数字组合的第一个数字放在a[k-1]中,当一个组合求出后,才将a[]中的一个组合输出。第一个数可以是mm-1……k,函数将确定的组合的第一个数字放入数组后,有两种可能的选择:若还未确定组合的其余元素,则继续递归去确定组合的其余元素;若已确定了组合的全部元素,则输出这个组合。

源文件:comb_integer_back.cpp

#include <stdio.h>

#include <stdlib.h>

 

#define MAX_N              100

 

int  g_pnA[MAX_N];

int  g_nR = 0;

 

int comb_integer_back(int nM, int nK)

{

     int    i = 0;

     int    j = 0;

 

     //回溯方法计算组合问题;

     i = 0;

     g_pnA[i] = 1;

     while(1){

            if(g_pnA[i]-i <= nM-nK+1)

            {

                   //向前试探;

                   if(i == nK-1)

                   {

                          //找到一个组合;

                          for(j=0; j<nK; j++)

                          {

                                 printf("%4d", g_pnA[j]);

                          }

                          printf("/r/n");

                          g_pnA[i] ++;

                          continue;

                   }

                   //向前试探;

                   i ++;

                   g_pnA[i] = g_pnA[i-1] +1;

            }

            else {

                   //回溯;

                   if(i == 0)

                   {

                          //找完全部解;

                          break;

                   }

                   i --;

                   g_pnA[i] ++;

            }

     }

     return     0;

}

 

int main(int argc, char* argv[])

{

     int    nM = 0;

     int    nK = 0;

……

     //回溯计算组合;

     comb_integer_back(nM, nK);

     return     0;

}

采用回溯法找问题的解,将找到的组合以从小到大顺序存放于A[0]A[1]A[R-1],组合的元素满足以下性质:

1A[I+1]>A[I],即后一个数字比前一个大;

2A[I]-I <= N-R+1

假设N=5R=2。按回溯算法思想,找解的过程可以叙述如下:首先放弃组合数个数为R的条件,候选组合从只有一个数字1开始。因该候选解满足除问题规模之外的全部条件,扩大其规模,并使用其满足上述条件(1),候选组合改为12。继续这一过程,得到候选组合123。该候选解满足包括问题规模在内的全部要求,因而是一个解。在该解基础上,选下一个候选解,因A[2]上的3调整为4,以及以后调整为5都满足问题的全部要求,得到解124125。由于对5不能再作调整,就要从A[2]回溯到A[1],这时的A[1]=2,可以调整为3,并向前试探,得到解134。重复上述向前试探和向后回溯的过程,直至要从A[0]再回溯时,说明已找完问题的全部解。

案例2-5 用贪婪法处理装箱问题

目标

学习贪婪法处理问题。

源代码:box_volume.cpp

////////////////////////////////////////////////////////////////////////

//

// box_volume.cpp: 使用贪婪法处理装箱问题案例源代码;

//

////////////////////////////////////////////////////////////////////////

 

#include <stdio.h>

#include <stdlib.h>

 

//物品信息结点;

typedef struct _ElmNodeTags

{

     int    nNO;                    //物品的编号;

     int    nVolume;        //物品的体积;

     struct _ElmNodeTags*  link; //后继物品的指针;

 

}ELMNODE;

 

//箱子信息结点;

typedef struct _BoxNodeTags

{

     int           nRemainder;   //箱子可用空间;

     ELMNODE*   head;              //箱内物品表头;

     ELMNODE*   tail;         //箱内物品表尾;

     struct _BoxNodeTags*  next;       //后继箱子指针;

 

}BOXNODE;

 

int main(int argc, char* argv[])

{

     BOXNODE*   pBoxHead = NULL;

     BOXNODE*   pBoxTail = NULL;

     BOXNODE*   pBox = NULL;

     BOXNODE*   pBoxT = NULL;

     ELMNODE*   pElmHead = NULL;

     ELMNODE*   pElmTail = NULL;

     ELMNODE*   pElm = NULL;

     ELMNODE*   pElmT = NULL;

     int           good_count = 0;

     int           box_count = 0;

     int           box_volume = 0;

     int           vol = 0;

     int           n = 0;

     int           i = 0;

 

     //获取输入信息;

     printf("Input the Box Volume: /r/n");

     scanf("%d", &box_volume);

     printf("Input the Count of Goods: /r/n");

     scanf("%d", &good_count);

 

     //输入所有的物品的体积(由大到小);

     printf("Input the volumes of goods ordered by high-to-low:/r/n");

     for(i=0; i<good_count; i++)

     {

            scanf("%d", &vol);

            pElm = (ELMNODE*)malloc(sizeof(ELMNODE));

            if(pElm == NULL)

            {

                   printf("Error memory, failed!/r/n");

                   return 0;

            }

            pElm->link = NULL;

            pElm->nVolume = vol;

            pElm->nNO = i;

            //加入到物品表中;

            if(pElmHead == NULL)

            {

                   pElmHead = pElm;

                   pElmTail = pElm;

            }

            else  {

                   pElmTail->link = pElm;

                   pElmTail = pElm;

            }

     }

     //采用贪婪法处理装箱问题(每个箱子尽可能的装入物品);

     pBoxHead = NULL;

     pBoxTail = NULL;

     box_count = 0;

     while(1){

            //取出物品表头的数据;

            pElm = pElmHead;

            if(pElm == NULL)

            {

                   break;

            }

            pElmHead = pElmHead->link;

            if(pElmHead == NULL)

            {

                   pElmTail = NULL;

            }

            pElm->link = NULL;

            vol = pElm->nVolume;

            //查找可用的箱子;

            for(pBox=pBoxHead; pBox!=NULL; pBox=pBox->next)

            {

                   if(pBox->nRemainder >= vol)

                   {

                          break;

                   }

            }

            //如果没有箱子,则创建新的并加入箱表中;

            if(pBox == NULL)

            {

                   pBox = (BOXNODE*)malloc(sizeof(BOXNODE));

                   if(pBox == NULL)

                   {

                          printf("Error Memory,Failed!/r/n");

                          return -1;

                   }

                   pBox->nRemainder = box_volume - vol;

                   pBox->head = NULL;

                   pBox->tail = NULL;

                   pBox->next = NULL;

                   //加入到箱子表中;

                   if(pBoxHead == NULL)

                   {

                          pBoxHead = pBox;

                          pBoxTail = pBox;

                   }

                   else  {

                          pBoxTail->next = pBox;

                          pBoxTail = pBox;

                   }

                   box_count ++;

            }

            //如果可用,则加入到箱子中;

            else  {

                   pBox->nRemainder -= vol;

            }

            //把物品转移到箱子中;

            if(pBox->head == NULL)

            {

                   pBox->head = pElm;

                   pBox->tail = pElm;

            }

            else  {

                   pBox->tail->link = pElm;

                   pBox->tail = pElm;

                   pElm->link = NULL;

            }

     }

    

     //输出计算的结果;

     printf("total box: %d/r/n", box_count);

     printf("goods in thd boxes were: /r/n");

     for(pBox=pBoxHead,i=0; pBox!=NULL; pBox=pBox->next,i++)

     {

            printf(" %2d th box, leave volume is %4d, the goods were:/r/n",

                   i, pBox->nRemainder);

            for(pElm=pBox->head; pElm!=NULL; pElm=pElm->link)

            {

                   printf("%4d(%d)", pElm->nNO+1, pElm->nVolume);

            }

            printf("/r/n");

     }

 

     //释放缓冲区;

     for(pBox=pBoxHead; pBox!=NULL; )

     {

            pBoxT = pBox->next;

            for(pElm=pBox->head; pElm!=NULL; )

            {

                   pElmT = pElm->link;

                   free(pElm);

                   pElm = pElmT;

            }

            pBox->head = NULL;

            pBox->tail = NULL;

            free(pBox);

            pBox = pBoxT;

     }

     return      0;

}

装箱问题可简述如下:设有编号为01N-1N种物品,体积分别为V(0)V(1)V(N-1)。将这N种物品装入容量都为V的若干箱子里。约定这N种物品的体积均不超过V,即对于0IN,有0<V(I)<V。不同的装箱方案所需要的箱子数目可能不同,装箱问题要求使装尽这N种物品的箱子数要少。

案例2-6 为参加网球比赛的选手安排循环比赛日程

目标

学习分治算法和回溯算法的设计技术。

源代码:game_date.cpp

////////////////////////////////////////////////////////////////////////

//

// game_date.cpp: 使用分治法/回溯法处理比赛的日程安排;

//

////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <string.h>

 

#define MAX_N          256  //最大的比赛人数(28次幂);

 

int  g_pnA[MAX_N+1][MAX_N];      //比赛的列表;

int  g_pnS[MAX_N][MAX_N];   //回溯的状态列表;

 

//

//使用分治法计算比赛日程表(比赛人数为2nK次幂);

//

int game_data_pice(int nK)

{

     int    twom1 = 0;

     int    twom = 0;

     int    col = 0;

     int    row = 0;

     int    m = 0;

 

     //预设两位选手的比赛日程;

     g_pnA[1][1] = 2;

     g_pnA[2][1] = 1;

     m = 1;

     twom1 = 1;

     //2m次幂的选手安排比赛日程;

     while(m < nK)

     {

            m ++;

            twom1 += twom1;

            twom = 2*twom1;

            //填日程表的左下角;

            for(row=twom1+1; row<=twom; row++)

            {

                   for(col=1; col<=twom1-1; col++)

                   {

                          g_pnA[row][col] = g_pnA[row-twom1][col] + twom1;

                   }

            }

            //填日程表右上角的第1;

            g_pnA[1][twom1] = twom1 + 1;

            for(row=2; row<=twom1; row++)

            {

                   g_pnA[row][twom1] = g_pnA[row-1][twom1] + 1;

            }

            //填日程表右上角的其它列(参照前一列填当前列);

            for(col=twom1+1; col<twom; col++)

            {

                   for(row=1; row<twom1; row++)

                   {

                          g_pnA[row][col] = g_pnA[row+1][col-1];

                   }

                   g_pnA[twom1][col] = g_pnA[1][col-1];

            }

            //参照右上角填日程表的右下角;

            for(col=twom1; col<twom; col++)

            {

                   for(row=1; row<=twom1; row++)

                   {

                          g_pnA[g_pnA[row][col]][col] = row;

                   }

            }

            //输出当前级别的日程表;

            for(row=1; row<=twom; row++)

            {

                   for(col=1; col<twom; col++)

                   {

                          printf("%4d", g_pnA[row][col]);

                   }

                   printf("/r/n");

            }

            printf("/r/n");

     }

 

     return      0;

}

 

//

//使用回溯法计算比赛日程表(nM为人数,nAll1,获取所有的组合);

//

int game_data_back(int nM, int nAll)

{

     int    col = 0;

     int    row = 0;

     int    val = 0;

     int    has = 0;

     int    i = 0;

     int    j = 0;

 

     //清理状态位;

     memset(g_pnS, 0, sizeof(int)*MAX_N*MAX_N);

 

     //A[0][0]位置开始回溯检查;

     row = 0;

     col = 0;

     while(1){

            //设置A[row][col]的元素,并检查是否重复;

            if(g_pnS[row][col] == 1)

            {

                   val = g_pnA[row][col] + 1;

            }

            else  {

                   val = 1;

            }

            for(; val<=nM; val++)

            {

                   //检查是否与该行前面的元素相同;

                   for(has=0,j=0; j<=col-1; j++)

                   {

                          if(g_pnA[row][j] == val)

                          {

                                 has = 1;

                                 break;

                          }

                   }

                   //检查是否与该列前面的元素相同;

                   for(i=0; (i<=row-1)&&(has==0); i++)

                   {

                          if(g_pnA[i][col] == val)

                          {

                                 has = 1;

                                 break;

                          }

                   }

                   if(has == 0)

                   {

                          break;

                   }

            }

            //如果没有可用的元素,则向后回溯;

            if(val > nM)

            {

                   g_pnS[row][col] = 0;

                   //如果没有可用的元素,则回溯到前一个元素;

                   if(row==0 && col==0)

                   {

                          //回溯到首位,全部结束;

                          break;

                   }

                   else if(col == 0)

                   {

                          //回退到上一行的尾列;

                          row --;

                          col = nM-1;

                   }

                   else  {

                          //回退到同行的前一列;

                          col --;

                   }

                   g_pnS[row][col] = 1;

            }

            //如果找到可用的元素,则向前试探;

            else  {

                   g_pnA[row][col] = val;

                   g_pnS[row][col] = 1;

                   if(row==nM-1 && col==nM-1)

                   {

                          //到行和列的尾部,找到一个组合;

                          for(i=0; i<nM; i++)

                          {

                                 for(j=0; j<nM; j++)

                                 {

                                        printf("%4d", g_pnA[i][j]);

                                 }

                                 printf("/r/n");

                          }

                          printf("/r/n");

                          //检查是否获取所有的组合;

                          if(nAll == 1)

                          {

                                 g_pnS[row][col] = 1;

                                 continue;

                          }

                          else  {

                                 break;

                          }

                   }

                   else if(col == nM-1)

                   {

                          //前进到下一行的首列;

                          row ++;

                          col = 0;

                   }

                   else  {

                          //前进到同行的后一列;

                          col ++;

                   }

                   g_pnS[row][col] = 0;

            }

     }

 

     return      0;

}

 

int main(int argc, char* argv[])

{

     int    nK = 0;

     int    nM = 0;

 

     //检查参数;

     if(argc < 2)

     {

            printf("Usage: game_data N(2<N<8)/r/n");

            return -1;

     }

     nK = atol(argv[1]);

     if((nK<2) || (nK>8))

     {

            printf("Usage: game_data N(2<N<8)/r/n");

            return -1;

     }

     nM = (int)pow(2, nK);

    

     //分治法计算组合;

     memset(g_pnA, 0, sizeof(int)*(MAX_N+1)*MAX_N);

     game_data_pice(nK);

    

     //回溯法计算组合;

     memset(g_pnA, 0, sizeof(int)*(MAX_N+1)*MAX_N);

     game_data_back(nM, 0);

     return      0;

}

上例中,通过分治法和回溯法解决同样的问题。不同的算法,计算复杂度和效率会有很大的不同。针对比赛日程表,分治法得到一个排列结果,回溯法可以得到所有的排列结果。算法的目的是得到正确的结果,有时候也需要计究效率。

案例2-7 求最长公共字符子序列

目标

学习动态规划算法的设计技术。

源代码:string_maxsubs.cpp

////////////////////////////////////////////////////////////////////////

//

// string_maxsubs.cpp: 使用动态规划法求最长公共字符子序列;

//

////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include <string.h>

 

#define MAX_N          100  //字符串的最大字节长度;

 

int  g_pnC[MAX_N][MAX_N];   //公共子序列的长度数组;

 

//

//使用动态规划法求最长公共字符子序列;

//

int string_max_subs(char* pstrA, char* pstrB, char* pstrO, int nLenO)

{

     int    nLenA = 0;

     int    nLenB = 0;

     int    nLenC = 0;

     int    i = 0;

     int    j = 0;

     int    k = 0;

 

     //计算最长公共子序列的长度;

     nLenA = strlen(pstrA);

     nLenB = strlen(pstrB);

     g_pnC[0][0] = 0;

     for(i=1; i<=nLenA; i++)

     {

            g_pnC[i][0] = 0;

     }

     for(j=1; j<=nLenB; j++)

     {

            g_pnC[0][j] = 0;

     }

     for(i=1; i<=nLenA; i++)

     {

            for(j=1; j<=nLenB; j++)

            {

                   if(pstrA[i-1] == pstrB[j-1])

                   {

                          g_pnC[i][j] = g_pnC[i-1][j-1] + 1;

                   }

                   else if(g_pnC[i-1][j] >= g_pnC[i][j-1])

                   {

                          g_pnC[i][j] = g_pnC[i-1][j];

                   }

                   else  {

                          g_pnC[i][j] = g_pnC[i][j-1];

                   }

            }

     }

     nLenC = g_pnC[nLenA][nLenB];

 

     //根据生成数组及长度反向构造序列;

     pstrO[nLenC] = '/0';

     i = nLenA;

     j = nLenB;

     k = nLenC;

     while(k > 0)

     {

            if(g_pnC[i][j] == g_pnC[i-1][j])

            {

                   i -- ;

            }

            else if(g_pnC[i][j] == g_pnC[i][j-1])

            {

                   j -- ;

            }

            else  {

                   k --;

                   pstrO[k] = pstrA[i-1];

                   i --;

                   j --;

            }

     }

     return      0;

}

 

int main(int argc, char* argv[])

{

     char strO[MAX_N];

     char strA[] = "Usage: string_maxsubs.cpp";

     char strB[] = "Usge: strng_maxsubs.cp";

     char strC[] = " subs.Usage :";

 

     memset(strO, 0, sizeof(strO));

     string_max_subs(strA, strB, strO, sizeof(strO));

     printf("(%s) and (%s):/r/n common max-length-substring is: (%s)/r/n",

            strA, strB, strO);

     printf("/r/n");

    

     memset(strO, 0, sizeof(strO));

     string_max_subs(strA, strC, strO, sizeof(strO));

     printf("(%s) and (%s):/r/n common max-length-substring is: (%s)/r/n",

            strA, strC, strO);

     printf("/r/n");

 

     return      0;

}

定义C[I][J]为序列(A0A1AI)和(B0B 1BJ)的最长公共子序列的长度,则计算C[I][J]可递归地表述如下:

C[I][J]=0,如果I=0J=0

C[I][J]= C[I-1][J-1]+1,如果IJ>0,且A[I-1]==B[J-1]

C[I][J]=MAXC[I][J-1]C[I-1][J]),如果IJ>0,且A[I-1]!=B[J-1]

 

根据上述算式可以得到计算两个序列最长公共子序列的长度的函数。可以看出,C[I][J]的产生仅依赖于C[I-1][J-1]C[I-1][J]C[I][J-1]。利用获取最长公共子序列长度的计算过程所产生的数组C[][],可以从C[M][N]开始,跟踪C[I][J]的产生方法,反向构造出最长公共子序列。


 

 

 

原创粉丝点击