最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和

参考:http://www.ahathinking.com/archives/124.html

最长公共子序列

1、动态规划解决过程

1)描述一个最长公共子序列

  如果序列比较短,可以采用蛮力法枚举出X的所有子序列,然后检查是否是Y的子序列,并记录所发现的最长子序列。如果序列比较长,这种方法需要指数级时间,不切实际。

  LCS的最优子结构定理:设X={x1,x2,……,xm}和Y={y1,y2,……,yn}为两个序列,并设Z={z1、z2、……,zk}为X和Y的任意一个LCS,则:

(1)如果xm=yn,那么zk=xm=yn,而且Zk-1是Xm-1和Yn-1的一个LCS。

  (2)如果xm≠yn,那么zk≠xm蕴含Z是是Xm-1和Yn的一个LCS。

  (3)如果xm≠yn,那么zk≠yn蕴含Z是是Xm和Yn-1的一个LCS。

  定理说明两个序列的一个LCS也包含两个序列的前缀的一个LCS,即LCS问题具有最优子结构性质。

2)一个递归解

  根据LCS的子结构可知,要找序列X和Y的LCS,根据xm与yn是否相等进行判断的,如果xm=yn则产生一个子问题,否则产生两个子问题。设C[i,j]为序列Xi和Yj的一个LCS的长度。如果i=0或者j=0,即一个序列的长度为0,则LCS的长度为0。LCS问题的最优子结构的递归式如下所示:

实现算法:

void lcs_length()
{
    int i,j;
    for(i=1;i<=X_LEN;i++)
        c[i][0]=0;
    for(i=0;i<=Y_LEN;i++)
        c[0][i]=0;
    for(i=1;i<=X_LEN;i++)
        for(j=1;j<=Y_LEN;j++)
    {
        if(s1[i-1]==s2[j-1])
        {
            c[i][j]=c[i-1][j-1]+1;
            b[i][j]=‘\\‘;
        }
        else if(c[i-1][j]>=c[i][j-1])
        {
            c[i][j]=c[i-1][j];
            b[i][j]=‘|‘;
        }
        else
        {
            c[i][j]=c[i][j-1];
            b[i][j]=‘-‘;
        }
    }
}

最长公共子串

动态规划有一个经典问题是最长公共子序列,但是这里的子序列不要求连续,如果要求序列是连续的,我们叫公共子串,那应该如何得到这个串呢?

最简单的方法就是依次比较,以某个串为母串,然后生成另一个串的所有长度的子串,依次去母串中比较查找,这里可以采用先从最长的子串开始,减少比较次数,但是复杂度依然很高!

然后重新看一下这个问题,我们建立一个比较矩阵来比较两个字符串str1和str2

定义 lcs(i,j) ,当str1[i] = str2[j]时lcs(i,j)=1,否则等于0。

example:

str1 = "bab"

str2 = "caba"

建立矩阵

--b  a  b

c 0  0  0

a 0  1  0

b 1  0  1

a 0  1  0

连续i子串的特点就是如果str1[i]和str2[j]是属于某公共子串的最后一个字符,那么一定有str1[i]=str2[j] && str1[i-1] = str2[j-1],从矩阵中直观的看,就是由“1”构成的“斜线”代表的序列都是公共子串那么最长公共子串肯定就是斜线“1”最长的那个串

那么现在问题就可以转化了,只要构造出如上的一个矩阵,用n^2的时间就可以得到矩阵,然后再到矩阵中去寻找最长的那个“1”构成的斜线就可以了!那么,现在又有了新的问题?如何快速的找到那个“1”构成的最长斜线呢?

采用DP的思想,如果str1[i] = str2[j],那么此处的包含str1[i] 和 str2[j]公共子串的长度必然是包含str1[i-1]和str2[j-1]的公共子串的长度加1,那么现在我们可以重新定义lcs(i,j),即是lcs(i,j) = lcs(i-1,j-1) + 1,反之,lcs(i,j) = 0。那么上面的矩阵就变成了如下的样子:

--b  a  b

c 0  0  0

a 0  1  0

b 1  0  2

a 0  2  0

现在问题又变简单了,只需要花n^2的时间构造这样一个矩阵,再花n^2的时间去找到矩阵中最大的那个值,对应的就是最长公共子串的长度,而最大值对应的位置对应的字符,就是最长公共子串的最末字符

算法还可以改进,我们可以将查找最大长度和对应字符的工作放在构造矩阵的过程中完成,一边构造一边记录当前的最大长度和对应位置,这样就节省了n^2的查找时间

实现算法:

/* 最长公共子串 DP */
int dp[30][30];
 
void LCS_dp(char * X, int xlen, char * Y, int ylen)
{
    maxlen = maxindex = 0;
    for(int i = 0; i < xlen; ++i)
    {
        for(int j = 0; j < ylen; ++j)
        {
            if(X[i] == Y[j])
            {
                if(i && j) //i和j都不为0的时候
                {
                    dp[i][j] = dp[i-1][j-1] + 1;
                }
                if(i == 0 || j == 0)
                {
                    dp[i][j] = 1;
                }
                if(dp[i][j] > maxlen)
                {
                    maxlen = dp[i][j]; //最长的子串长度
                    maxindex = i + 1 - maxlen;//求出公共子串开始的位置
                }
            }
        }
    }
    outputLCS(X);
}

最长重复子串

最长不重复子串

从第二个字符开始逐个加入不重复子串中,如果当前与已有的不重复字符串中字符存在重复的,则此字符不能加入,此时,需要计算最大不重复的长度,然后将不重复串的第一个下标更新为重复字符的下一个。

int lengthOfLongestSubstring(string s)
    {
        if(s.empty())
            return 0;
        int maxLen=1;
        int i,j,k;
        j=0;
        k=0;
        for(i=1; i<(int)s.size(); i++)
        {
            j=k;
            while(j<i)
            {
                if(s[i]!=s[j])
                    j++;
                else
                {
                    if(i-k>maxLen)
                        maxLen=i-k;
                    k=j+1;
                    break;
                }
            }
        }
        if(i-k>maxLen)
            maxLen=i-k;
        return maxLen;
    }

最长回文子串

此可以使用动态规划实现,leetcode上的题目。

    string longestPalindrome(string s)
    {
        if(s.empty())
            return NULL;
        int start=0;
        int end=0;
        int n=s.length();
        bool dp[n][n];
        memset(dp,false,sizeof(dp));
        int i;
        dp[0][0]=true;
        for(i=1;i<n;i++)
        {
            dp[i][i]=true;
            dp[i][i-1]=true;
        }
        int k;//k用于记录从i开始的子串的长度,当长度为1是肯定是回文,从len=2开始判断
        for(k=2;k<=n;k++)
        {
            for(i=0;i<=n-k;i++)
            {
                if(s[i]==s[i+k-1]&&dp[i+1][i+k-2])
                {
                    dp[i][i+k-1]=true;
                    if(k>end-start+1)
                    {
                        start=i;
                        end=i+k-1;
                    }
                }
            }
        }
        return s.substr(start,end-start+1);
    }

最长递增子序列

方法一:DP

像LCS一样,从后向前分析,很容易想到,第i个元素之前的最长递增子序列的长度要么是1(单独成一个序列),要么就是第i-1个元素之前的最长递增子序列加1,可以有状态方程:

LIS[i] = max{1,LIS[k]+1},其中,对于任意的k<=i-1,arr[i] > arr[k],这样arr[i]才能在arr[k]的基础上构成一个新的递增子序列。

int LIS(int arr[],int n)
{
    int *dp=new int[n];
    for(int i=0;i<n;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;
        }  }
}

最大子数组和(连续子数组最大和)

可能有两种情况,一种是如果和全是负数的时候是返回0还是数组中的最小的一个负数呢?在于数组的元素是不是必须选或者不选。可以看看http://blog.csdn.net/v_july_v/article/details/6444021

如果数组中的元素非要选择,实现代码:

int maxSubArray(int A[], int n) {
        if(n==0)
            return 0;
        int maxSum=A[0];
        int sum=A[0];
        int i;
        for(i=1;i<n;i++)
        {
            if(sum<0)
                sum=A[i];
            else
                sum+=A[i];
            if(maxSum<sum)
            {
                maxSum=sum;
            }
        }

        return maxSum;
    }

否则,可以令sum和maxSum=0.

时间: 2024-12-12 16:13:57

最长公共子序列|最长公共子串|最长重复子串|最长不重复子串|最长回文子串|最长递增子序列|最大子数组和的相关文章

Manacher算法----最长回文子串

题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些的子串,那么对子串的回文判断其实是不需要的.同时,奇数和偶数长度还要分别考虑. Manacher算法可以解决上述问题,并在O(n)时间复杂度内求出结果.下面我们来看一下Manacher算法. 首先,为了处理奇偶的问题,在每个字符的两边都插入一个特殊的符号,这样所有的奇数或偶数长度都转换为奇数长度.比

最长回文子串(转自:hihoCoder)

#1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:"小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?" 小Ho奇怪的问道:"什么叫做最长回文子串呢?" 小Hi回答道:"一个字符串中连续的一

manacher求最长回文子串算法

原文:http://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个特殊的符号.比如 abba 变成 #a#b#b#a#, aba变成 #a#b#a#. 为了进一步减少编码的复杂度,可以在字符串的开始加入另一个特殊字符,这样就不用特殊处理越界问题,比如 @#a#b#a#(注意,下面的代码是用C语言写 就,由于C语言规范还要求字符串末尾有一个'\0'所以正好OK,但其

最长回文子串 - Manacher算法

算法思想: 设有字符串s[] = "121" 第一步:通过在每个字符左右都添加一个特殊字符,把奇数长度和偶数长度的字符串都转化成奇数(例如. "121" 加上特殊字符后变成"#1#2#1" ),同时也可在开头再加一个特殊字符,以便于忽略越界问题(如上例"121"变成"$#1#2#1#"  此时开头的特殊字符$和字符串末尾的\0与此串中其他字符都不同,即可忽略越界问题),此时字符串变成 s[] = "

#1032 : 最长回文子串

描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?” 小Ho奇怪的问道:“什么叫做最长回文子串呢?” 小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身

hiho一下 第一周 最长回文子串

时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?” 小Ho奇怪的问道:“什么叫做最长回文子串呢?” 小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往

[LeetCode] 647. 回文子串 ☆☆☆(最长子串、动态规划、中心扩展算法)

描述 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串. 示例 1: 输入: "abc"输出: 3解释: 三个回文子串: "a", "b", "c".示例 2: 输入: "aaa"输出: 6说明: 6个回文子串: "a", "a", "a", "

HDU 1544 Palindromes(回文子串)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1544 问题分析: 问题要求求出字符串的连续子串中的回文子串个数.首先,需要区分连续子串与子序列的区别. 连续子串为连续的字符组成的字符串:子序列需要满足在子序列中出现的字符的相对顺序与字符串中出现的相对顺序相同. 问题的解法:根据回文子串的长度分为奇数与偶数分为两种可能: 1.当回文子串长度为奇数时,中间的字符为任意字符,取除了字符串最左边与最右边的字符的其他字符,通过向两边拓展来判断 奇数回文子串

最长回文子序列(不连续)以及最长回文子串(连续)

整理了一下关于回文子序列和回文子串的程序. 其中(1)和(2)是采用动态规划的思想写出的回文子序列的程序,这种子序列就是在原始的串中可以不连续,比如对于那种要求删除几个字符来得到最长的回文字符串的题就是这种情况. 比如caberbaf.  最长的子序列是5 abeba 或者abrba.而子串最长只有1 (3)(4)(5)都是最长子串的求法.(3)是暴力求解,(4)是改进的暴力求解.(5)采用的是动态规划的方法. 1 #include <iostream> 2 #include <stri