最大子序和&最长公共子序列&最长公共子串 超全模板

1.最大子序和 
常量空间线性时间的联机算法几乎是完美的算法。

#include<stdio.h>

#include<string.h>

#include<iostream>

using namespace std;

 

void getlen(int a[],int n){

int tt=-99999;

int start=0;//起始位置

int end=0;//结束位置

int sum=0;

int k=0;

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

{

sum+=a[i];

if(tt<sum)

{

tt=sum;

end=i;

start=k;

}

if(sum<0)

{

sum=0;

k=i+1;

}

}

printf("%d %d~%d\n",tt,start,end);

}

 

int main()

{

int len;

int a[10];

cin>>len;

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

cin>>a[i];

getlen(a,len);

 return 0;

}

 

2.最长公共子序列(可不连续)LCS

//最长公共子序列(可不连续)    

#include  <iostream>    

#include  <cstdio>    

#include  <cstring>    

using  namespace  std;    

int  dp[105][105];    //记录当前字母“前面”的最长子序列的长度    

//char a[100],b[100];

int  path[150];    

int  main()    

{    

    char a[20]="bcbcbab";

char b[20]="cccabcc";

//cin>>a>>b;

   int  len1  =  strlen(a);    

   int  len2  =  strlen(b);    

   memset(dp,0,sizeof(dp));

    for(int  i  =  1;  i  <=  len1;  i++)    //i,j从一开始    

        for(int  j  =  1;  j  <=  len2;  j++)    

            {    

                if(a[i-1]  ==  b[j-1])                //前一个相同,当前的就是前面dp+1;    

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

                else    

                dp[i][j]  =  max(dp[i-1][j],dp[i][j-1]);    

            }    

    cout << dp[len1][len2] << endl;    

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

{

for(int j=0;j<=len2;j++)

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

        printf("\n");

}

//输出路径,只会输其中一条(如果有多条路径的话);从a字符串里面输出。

//关于为什么这么输出,可以参考上面输出的dp数组,可加深对LCS求解过程的理解

    int i = len1, j = len2, k = dp[len1][len2];    //k代表最长的数量;ij都是从后往前找;

    while(i > 0)    

    {    

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

i--;  

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

j--;

        else    

        {      

            path[k--] = i-1;                    

i--,j--;   

        }    

    }  

    for(int i =  1;  i <= dp[len1][len2];  i++)  //从前往后输出    

    {    

        printf("%c",a[path[i]]);    

    }    

    printf("\n");

    

        

    //输出路径,只会输其中一条(如果有多条路径的话);从b字符串里面输出  

    i  =  len1,  j  =  len2,  k  =  dp[len1][len2];   

    while(j > 0)

    {

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

j--;

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

i--;

        else    

        {      

            path[k--] = j-1;                    

i--,j--;   

        }    

    }         

for(int i = 1; i <= dp[len1][len2]; i++)

    {

        printf("%c",b[path[i]]);

    }

    printf("\n");

    

    

    return 0;

}   

 

3.最长公共子串(连续)

[cpp] view plain copy 

1. #include <iostream>  

2. #include <cstdio>  

3. #include <cstring>  

4. using namespace std;  

5. int dp[105][105];  //记录当前字母“前面”的最长子序列的长度  

6. char a[100], b[100];  

7. int main()  

8. {  

9.     while(cin >> a >> b)  

10.     {  

11.         int max1 = 0, temp;  

12.         memset(dp,0,sizeof(dp));  

13.         int len1 = strlen(a), len2 = strlen(b);  

14.         for(int i = 1; i <= len1; i++)  

15.             for(int j = 1; j <= len2; j++)  

16.             {  

17.                  if(a[i-1] == b[j-1])  dp[i][j] = dp[i-1][j-1] + 1;  

18.                  else dp[i][j] = 0;  

19.                  if(max1 < dp[i][j])  

20.                  {  

21.                     max1 = dp[i][j]; //纪录dp[][]中的最大值  

22.                     temp = i;//纪录最长公共子串的末端在str1中的位置(也可以纪录在str2中的位置)  

23.                  }  

24.             }  

25.         for(int i = temp - max1; i < temp; i++)  

26.             cout << a[i];  

27.             cout << endl;  

28.     }  

29.   

30. }  


4.LIS最长上升子序列的三种写法

方法一:转换成LCS问题 空间时间复杂度都比较大, 空间n^2,时间n^2+nlogn
将原数组与排好序的数组进行求LCS,得到的最长公共子序列的就是最长上子序列.

[cpp] view plain copy 

1. #include<cstdio>  

2. #include<cstring>  

3. #include<iostream>  

4. #include<algorithm>  

5. using namespace std;  

6. const int maxn = 10005;  

7. int a[maxn], b[maxn], dp[maxn][maxn];  

8. int main(void)  

9. {  

10.     int n;  

11.     while(cin >> n)  

12.     {  

13.         memset(dp, 0, sizeof(dp));  

14.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), b[i] = a[i];  

15.         sort(b, b+n);  

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

17.             for(int j = 1; j <= n; j++)  

18.             {  

19.                 if(a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1] + 1;  

20.                 else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);  

21.             }  

22.         printf("%d\n", dp[n][n]);   //非严格递增,如要严格递增要对b数组去重  

23.     }  

24.     return 0;  

25. }  

方法二:动态规划  时间复杂度n^2,空间复杂度n
f(i)表示L中以ai为末元素的最长递增子序列的长度。则有如下的递推方程:
这个递推方程的意思是,在求以ai为末元素的最长递增子序列时,找到所有序号在L前面
且小于ai的元素aj,即j<iaj<ai。如果这样的元素存在,那么对所有aj,都有一个以aj为末元素的最长递增子序列的长度f(j),把其中最大的f(j)选出来,那么f(i)就等于最大的f(j)加上1,即以ai为末元素的最长递增子序列,等于以使f(j)最大的那个aj为末元素的递增子序列最末再加上ai;如果这样的元素不存在,那么ai自身构成一个长度为1的以ai为末元素的递增子序列。

[cpp] view plain copy 

1. #include<iostream>  

2. #include<cstdio>  

3. using namespace std;  

4. const int maxn = 10005;  

5. int a[maxn], dp[maxn], ans;  

6. int main(void)  

7. {  

8.     int n;  

9.     while(cin >> n)  

10.     {  

11.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;  

12.         ans = 1;  

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

14.             for(int j = 0; j < i; j++)  

15.                 if(a[j] < a[i] && dp[j]+1 > dp[i])  

16.                     dp[i] = dp[j]+1, ans = max(ans, dp[i]);  

17.         printf("%d\n", ans);  

18.     }  

19.     return 0;  

20. }  


方法三:方法二+二分 时间复杂度nlongn
在第二种算法中,在计算每一个f(i)时,都要找出最大的f(j)(j<i)来,由于f(j)没有顺序,只能顺序查找满足aj<ai最大的f(j),如果能将让f(j)有序,就可以使用二分查找,这样算法的时间复杂度就可能降到O(nlogn)。于是想到用一个数组B来存储子序列的最大增子序列的最末元素,即有B[f(j)] = aj在计算f(i)时,在数组B中用二分查找法找到满足j<iB[f(j)]=aj<ai的最大的j,并将B[f[j]+1]置为ai

[cpp] view plain copy 

1. #include<iostream>  

2. #include<cstdio>  

3. #include<cstring>  

4. using namespace std;  

5. const int maxn = 1005;  

6. int a[maxn], b[maxn], dp[maxn];  

7. int main(void)  

8. {  

9.     int n;  

10.     while(cin >> n)  

11.     {  

12.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;  

13.         int Len = 1, l, m, r;  

14.         b[1] = a[0];  

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

16.         {  

17.             l = 1, r = Len;  

18.             while(l <= r)  

19.             {  

20.                 m = (l+r)/2;  

21.                 if(b[m] < a[i]) l = m+1;  

22.                 else r = m-1;  

23.             }  

24.             b[l] = a[i];  

25.             if(l > Len) Len++;  

26.         }  

27.         printf("%d\n", Len);  

28.     }  

29.     return 0;  

30. }  


上面的代码都是只求了最长的长度,但没有求路径。路径可能有多条,题目不同要求输出的不同,贴一下输出第一条路径和最后一条路径的代码。输出第一条符合的路径:

[cpp] view plain copy 

1. #include<iostream>  

2. #include<cstdio>  

3. using namespace std;  

4. const int maxn = 10005;  

5. int a[maxn], dp[maxn], pre[maxn], path[maxn],  ans, e;  

6. int main(void)  

7. {  

8.     int n;  

9.     while(cin >> n)  

10.     {  

11.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1, pre[i] = -1;  

12.         ans = 1;    //初始最大长度为1,结尾在a[0]  

13.         e = 0;  

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

15.             for(int j = 0; j < i; j++)  

16.                 if(a[j] < a[i] && dp[j]+1 > dp[i])  

17.                 {  

18.                     dp[i] = dp[j]+1;  

19.                     pre[i] = j;     //记录每个点i的上一个最长序列,存在已他自己为下标的pre里  

20.                     if(dp[i] > ans) ans = dp[i], e = i;    //因为是>,而不是>=所以最后一个元素肯定是所有里面最前面的  

21.                 }  

22.         printf("%d\n", ans);  

23.         //路径要逆推回去  

24.         for(int i = 0, k = ans; i < ans; i++)  

25.         {  

26.             path[k--] = a[e];   //从后往前推,最后一个节点为e;把他的值a【e】输出了  

27.             e = pre[e];     //pre【e】存了上一个他的节点,也就是他之前最长的长度的最后一个字母  

28.         }  

29.         for(int i = 1; i <= ans; i++)  

30.         {  

31.             if(i-1) printf(" ");  

32.             printf("%d", path[i]);  

33.         }  

34.         printf("\n");  

35.     }  

36.     return 0;  

37. }  

 

输出最后一条路径

[cpp] view plain copy 

1. #include<iostream>  

2. #include<cstdio>  

3. using namespace std;  

4. const int maxn = 10005;  

5. int a[maxn], dp[maxn], path[maxn];  

6. int main(void)  

7. {  

8.     int n, ans;  

9.     while(cin >> n)  

10.     {  

11.         ans = 0;  

12.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;  

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

14.             for(int j = 0; j < i; j++)  

15.                 if(a[j] < a[i] && dp[j]+1 > dp[i])  

16.                     dp[i] = dp[j]+1, ans = max(ans, dp[i]);  

17.         printf("%d\n", ans);  

18.         for(int i = n-1, j = ans; i > -1; i--)   //从后往前找,第一个找的肯定是最后面那个最长的最后一个元素  

19.             if(dp[i] == j) path[j--] = a[i];  

20.         for(int i = 1; i <= ans; i++)  

21.         {  

22.             if(i != 1) printf(" ");  

23.             printf("%d", path[i]);  

24.         }  

25.         printf("\n");  

26.     }  

27.     return 0;  

28. }  

 

//求原串与逆序串的最大公共子序列(滚动数组)

[cpp] view plain copy 

1. #include<iostream>  

2. #include<cstdio>  

3. using namespace std;  

4. const int maxn = 10005;  

5. int a[maxn], dp[maxn], path[maxn];  

6. int main(void)  

7. {  

8.     int n, ans;  

9.     while(cin >> n)  

10.     {  

11.         ans = 0;  

12.         for(int i = 0; i < n; i++) scanf("%d", &a[i]), dp[i] = 1;  

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

14.             for(int j = 0; j < i; j++)  

15.                 if(a[j] < a[i] && dp[j]+1 > dp[i])  

16.                     dp[i] = dp[j]+1, ans = max(ans, dp[i]);  

17.         printf("%d\n", ans);  

18.         for(int i = n-1, j = ans; i > -1; i--)   //从后往前找,第一个找的肯定是最后面那个最长的最后一个元素  

19.             if(dp[i] == j) path[j--] = a[i];  

20.         for(int i = 1; i <= ans; i++)  

21.         {  

22.             if(i != 1) printf(" ");  

23.             printf("%d", path[i]);  

24.         }  

25.         printf("\n");  

26.     }  

27.     return 0;  

28. }  

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值