Fork me on GitHub

最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离等问题

最大子序列
int maxSubSum(const vector<int> & arr,int &begin,int &end){
    int maxSum=0;
    int currSum=0;
    int newbegin=0;
    for(int i=0;i<arr.size();++i){
        currSum+=arr[i];
        if(currSum>maxSum){
            maxSum=currSum;
            begin=newbegin;
            end=i;
        }
        if(currSum<0){
            currSum=0;
            newbegin=i+1;
        }
    }
    return maxSum;
}
最长递增子序列
//动态规划法求最长递增子序列 LIS  
int dp[101]; /* 设数组长度不超过100,dp[i]记录到[0,i]数组的LIS */
int lis;    /* LIS 长度 */
int LIS(int * arr, int size)
{
    for (int i = 0; i < size; ++i)
    {
        dp[i] = 1;
        for (int j = 0; j < i; ++j)
        {
            if (arr[i] > arr[j] && dp[i] < dp[j] + 1)
            {
                dp[i] = dp[j] + 1;
                if (dp[i] > lis)
                {
                    lis = dp[i];
                }
            }
        }
    }
    return lis;
}

•     
两个字符串的最长连续公共子串:
1.解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.
2.但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。
3.这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了。因此我们只需使用一维数组即可。

最长公共子序列:
最长公共子序列是一个十分实用的问题,它可以描述两段文字之间的"相似度"。设有二维数组f[i,j] 表示 X 的 i 位和 Y 的 j 位之前的最长公共子序列的长度。
 
记录路径的方法:递归的调用: 
if(c[i][j]==(c[i-1][j-1]+1)){ //这样判断不需要额外空间记录
            lcs(c,x,i-1,j-1);
            //注意c的长度要比x大1
            Cout<<x[i-1];
        }else if(c[i][j]==c[i-1][j]){
            lcs(c,x,i-1,j);
        }else{
            lcs(c,x,i,j-1);
        }
int getMaxLengthSub(string s1, string s2)
{
    int ret = 0;
    vector<vector<int> > vecs(s1.size() + 1, vector<int>(s2.size() + 1, 0));
    for (int i = 1; i <= s1.size(); i++)
    {
        for (int j = 1; j <= s2.size(); j++)
        {
            if (s1[i - 1] == s2[j - 1]) //下标
            {
                vecs[i][j] = vecs[i - 1][j - 1] + 1;
            }
            else
            {
                vecs[i][j] = max(vecs[i - 1][j], vecs[i][j - 1]);
            }
        }
    }
    ret = vecs[s1.size()][s2.size()];
    return ret;
}

 

LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。引入:

LCS(Longest Common Subsequence) 就是求两个字符串最长公共子串的问题。

比如:

  String str1 = new String("adbccadebbca");
  String str2 = new String("edabccadece");
str1与str2的公共子串就是bccade.

解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长的1序列,其对应的位置就是最长匹配子串的位置.

 

 下面是字符串21232523311324和字符串312123223445的匹配矩阵,前者为X方向的,后者为Y方向的。不难找到,加粗部分是最长的匹配子串。通过查找位置我们得到最长的匹配子串为:21232

      2 1 23 2 5 23 3 1 13 2 4
 3 0 0 0 1 0 0 0 1 1 0 0 1 0 0 
 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 
 2 1 0 1 0 1 0 1 0 0 0 0 0 1 0 
 1 0 1 0 0 0 0 0 0 0 1 1 0 0 0 
 2 1 0 1 0 1 0 1 0 0 0 0 0 1 0 
 3 0 0 0 1 0 0 0 1 1 0 0 1 0 0 
 2 1 0 1 0 1 0 1 0 0 0 0 0 1 0 
 2 1 0 1 0 1 0 1 0 0 0 0 0 1 0 
 3 0 0 0 1 0 0 0 1 1 0 0 1 0 0 
 4 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
 4 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
 5 0 0 0 0 0 1 0 0 0 0 0 0 0 0 
但是在0和1的矩阵中找最长的1对角线序列又要花去一定的时间。通过改进矩阵的生成方式和设置标记变量,可以省去这部分时间。下面是新的矩阵生成方式: 
  0 0 0 1 0 0 0 1 1 0 0 1 0 0 
  0 1 0 0 0 0 0 0 0 2 1 0 0 0 
  1 0 2 0 1 0 1 0 0 0 0 0 1 0 
  0 2 0 0 0 0 0 0 0 1 1 0 0 0 
  1 0 3 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 4 0 0 0 2 1 0 0 1 0 0 
  1 0 1 0 5 0 1 0 0 0 0 0 2 0 
  1 0 1 0 1 0 1 0 0 0 0 0 1 0 
  0 0 0 2 0 0 0 2 1 0 0 1 0 0 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 0 0 0 0 0 0 0 0 1 
  0 0 0 0 0 1 0 0 0 0 0 0 0 0 
当字符匹配的时候,我们并不是简单的给相应元素赋上1,而是赋上其左上角元素的值加一。我们用两个标记变量来标记矩阵中值最大的元素的位置,在矩阵生成的过程中来判断当前生成的元素的值是不是最大的,据此来改变标记变量的值,那么到矩阵完成的时候,最长匹配子串的位置和长度就已经出来了。

  这样做速度比较快,但是花的空间太多。我们注意到在改进的矩阵生成方式当中,每生成一行,前面的那一行就已经没有用了。因此我们只需使用一维数组即可。最终的代码如下:(源代码有些许瑕疵,已改进)

 

void getLCS(char* str1, char* str2)
{
    int len1 = strlen(str1);
    int len2 = strlen(str2);
    int *matrix = new int[len1]; //str1为x方向
    //初始化matrix
    for (int i = 0; i < len1; i++)
    {
        matrix[i] = 0;
    }
    int *maxValue = new int[len2];//str2为y方向,最多有len2个最大值
    int *maxIndex = new int[len2];
    //初始化maxValue和maxIndex
    for (int i = 0; i < len2; i++)
    {
        maxValue[i] = -1;
        maxIndex[i] = -1;
    }
    for (int i = 0; i < len2; i++)
    {
        for (int j = len1 - 1; j >= 0; j--)
        {
            //扫描str1与str2的各个字符的匹配情况
            if (str2[i] == str1[j])
            {
                if (j == 0)
                {
                    matrix[j] = 1;
                }
                else
                {
                    matrix[j] = matrix[j - 1] + 1;
                }
            }
            else
            {
                matrix[j] = 0;
            }
            if (matrix[j] != 0 && matrix[j] > maxValue[0])
            {
                //更新maxValue的值
                maxValue[0] = matrix[j];
                maxIndex[0] = j;
                //将其他的maxValue重置
                for (int i = 1; i < len2; i++)
                {
                    maxValue[i] = -1;
                    maxIndex[i] = -1;
                }
            }
            else if (matrix[j] == maxValue[0])//有多个最大连续公共子串
            {
                for (int i = 1; i < len2; i++)
                {
                    if (maxValue[i] == -1)
                    {
                        maxValue[i] = matrix[j];
                        maxIndex[i] = j;
                        break; //只需加一个
                    }
                }
            }
        }
    }
    for (int i = 0; i<len2; i++)
    {
        if (maxValue[i]>0)
        {
            cout << "" << i + 1 << "个公共子串" << endl;
            for (int j = maxIndex[i] - maxValue[i] + 1; j <= maxIndex[i]; j++)
            {
                cout << str1[j];
            }
            cout << endl;
        }
    }
}

 reference:程序员面试100题之七:最长公共子字符串

 

posted @ 2016-09-08 18:32  ranjiewen  阅读(1511)  评论(0编辑  收藏  举报