致我们终将忘记的算法(随处可见的经典<1>)

1->求连续子序列的最大和问题。例如【0,-2,3,5,-1,2】返回值9。

*解题方法:这是动态规划算法的经典题目:该算法的过程描述为,只要前i项的和还没有小于0,那么子序列就一直往后扩展,否则丢弃之前的子序列开始新的子序列。同时我们也要记录下各个子序列的和,以便求出子序列和的最大值。

int MaxSum(int A[],int n){

    int Maxsum=INT_MIN;

    int sum=0;

    for(int i=0;i<n;i++){

        if(sum>=0)  sum+=A[i];

        else sum=A[i];

        if(sum>Maxsum)  Maxsum=sum;

    }

    return Maxsum;

}

另外要注意该题目对最大和为负数的处理方法是直接返回0.如果题目要求对最大和为负数的时候返回最小负数。那么就在返回结果之前,采用顺序查找的方式,在数组里面找出最小负数,然后将其返回。

 

2->对于上述的题目,如果题目要求保存最大和子序列的开始位置和结束位置?

解题方法:将上述的代码稍微作下修改,即可

int MaxSum_Locate(int A[],int n,int &start,int &end){

    int Maxsum=INT_MIN;

    int sum=0;   start=end=0;

    int curlocate=0;

    for(int i=0;i<n;i++){

        if(sum<0){sum=A[i]; curLocate=i;}

        else sum+=A[i];

         if(sum>MaxSum){

              MaxSum=sum;   start=curLocate;  end=i;

         }

         return Maxsum;

    }

}

 

3->连续子序列的最大和,也可以采用分治的算法。对于一个数组求子序列的最大和,只有三种情况:1):最大和出现在数组的右半部分 2):最大和出现在数组的左半部分 3):跨越了两个部分

下面就给出连续子序列的最大和的分治算法求解:

int MaxSumRec(const vector<int> & A,int left,int right){

    if(left==right){

        if(A[left]>0) return A[left];

        else return 0;

    }

    int mid=(left+right)/2;

    int maxLeft=MaxSumRec(A,left,mid);

    int maxRight=MaxSumRec(A,mid+1,right);

    int leftTmp=0,rightTmp=0;

    int maxLeftTmp=0,maxRightTmp=0;

    for(int i=mid;i>=left;i--){

        leftTmp+=A[i];   if(leftTmp>maxLeftTmp) maxLeftTmp=leftTmp;

    }

    for(int i=mid+1;i<=right;i++){

        rightTmp+=A[i];  if(rightTmp>maxRightTmp) maxRightTmp=rghtTmp;

    }

    return Max(maxLeft,maxRight,maxLeftTmp+maxRightTmp);

}

 

4->最长递增子序列

解题方法1:动态规划的求解方法:根据分析可以知道第i个元素的最长递增序列要么是1,那么是第i-1个元素之前的元素的最长递增加1.

int Lis(int A[],int n){

    int *dp=new int[n];

    for(int i=0;i<n;i++) dp[i]=1;      //动态规划数组初始化

    for(int i=1;i<n;i++)

        for(int j=0;j<i;j++){

             if(A[i]>A[j] && dp[i]<dp[j]+1)   dp[i]=dp[j]+1;

        }

    int ret=0;

    for(int i=0;i<n;i++)   if(dp[i]>ret)  ret=dp[i];

    return ret;

}

解题方法2:转化为求最长公共子序列问题:将原来的数组排序,排序后的数组与原来数组的最长公共子序列,就是最大递增序列

int LCS(int A[],int n){

    int *B=new int[n];

    copy(A,A+n,B);    sort(B.B+n);

    int **dp=new int*[n];

    for(int i=0;i<n;i++)  dp[i]=new int[n]();

    for(int i=0;i<n;i++)

       for(int j=0;j<n;j++){

            if(A[i]==B[j]) d[i][j]=dp[i-1][j-1]+1;

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

            else dp[i][j]=dp[i][j-1];

       }

     return dp[n-1][n-1];

}

 

5->最长公共子串问题

int LCS(const int *str1,int len1,const char *str2,int len2,char *&lcs){

    if(str1==NULL || str2==NULL) return -1;

    int *c=new int [len2]();

    int max_len=0;     int end=0;//保存最大公共子串的末尾位置

    for(int i=0;i<len1;i++)

        for(int j=len2-1;j>=0;j--){

              if(str1[i]==str2[j]){

                  c[j]=c[j-1]+1;

                  if(c[j]>max_len){max_len=c[j];  end=j;}

              }else {

                  c[j]=0;

              }

        }

        if(max_len==0)  return 0;

        //得到公共子串

        lcs=new char[max_len+1];

        for(int i=0;i<max_len;i++)  lcs[i]=str2[end-max_len+1-i];

        lcs[max_len]='\0';

        return max_len;

}

 

6->最长公共子序列问题

解题方法:递归的方式求解问题:设字符串a[0....n],b[0.....m],则易知当a[i]与b[j]相同时,则求两个串从下一个位置开始剩下部分的最长公共子序列即可,当u相同时,最长公共子序列表示为LCS(i,j+1)和LCS(i+1,j)中两则较大的。

Code:

int LCS(int &A[],int i,int &B[],int j){

    if(i>=A.size() ||j>=B.szie())   return 0;

    if(a[i]==b[j])  return 1+LCS(A,i+1,B,j+1);

    else return max(LCS(A,i+1,B,j),LCS(A,i,B,j+1));

}

解题方法2:动态规划方法,采用一个二维数组来保存中间结果

Code:

int LCS(int &A[],int &B[]){

    int **dp=new int *[A.Size()+1];  

    for(int i=0;i<=A.size();i++)   dp[i]=new int[B.szie()+1]();

    for(int i=0;i<A.size();i++)

        for(int j=0;j<B.size();j++){

             if(a[i]==b[j])   dp[i+1][j+1]=d[i][j]+1;

             else if(dp[i+1][j]>dp[i][j+1])  dp[i+1][j+1]=dp[i+1][j];

             else  dp[i+1][j+1]=dp[i][j+1];

        }

        return dp[A.size()][B.size()];

}

如果需要给出具体的子序列,那么就在动态规划算法里面加入一个标记数组flag[n][m],当a[i]==b[j]时,flag[i][j]=1;当取a向前一步是flag[i][j]=2,还有一个等于3.取最长子列的程序:

void getLCS(vector<char> &lcs){

    int i=a.size(),j=b.size();

    while(i>0 &&j>0){

        if(flag[i][j]==1){ lcs.insert(lcs.begin(),a[i-1]);i--;j-- ; }

        else if(flag[i][j]==2) i--;

        else j--;

    }

    print(lcs.begin(),lcs.end());

}

 

7->字符串编辑距离问题:给一个源字符串和,目标字符串,能够对源串进行:在给定位置插入一个字符,替换一个字符,删除一个字符。求源串到目的串的最小步长

解题方法:最小编辑距离问题的递归式:f(i,j)=MIN(f(i-1,j)+1  , f(i,j-1)+1, f(i-1,j-1)+(strA[i]==strB[j]?0:1)); 

Code:

/*C数组被初始化为INT_MIN*/

int  EDM(char *&strA,int i,char *&strB,int j){

    if(i>strA.size() || j>strB.size()){

         if(i>strA.size() && strB.size())  {return 0;}

         else  return MAX(strA.size()-i+1,strB.size()-j+1);

    }

    int a1=0,a2=0;

    if(strA[i]==strB[j])

        a1=EDM(strA,i+1,strB,j+1);

    else{

         int b1=EDM(strA,i+1,strB,j);

         int b2=EDM(strA,i,strB,j+1);

         int b3=EDM(strA,i+1,strB,j+1);

        a2=min(b1,b2,b3)+1;

    }

    return min(a1,a2);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值