动态规划算法之: 最长公共子串

最长公共子串与最长公共子序列区别:
找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。而最长公共子序列则并不要求连续。
其实这是一个序贯决策问题,可以用动态规划来求解。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:”bab”和”caba”(当然我们现在一眼就可以看出来最长公共子串是”ba”或”ab”)

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  1

a  0  1  0

我们看矩阵的斜对角线最长的那个就能找出最长公共子串。

不过在二维矩阵上找最长的由1组成的斜对角线也是件麻烦费时的事,下面改进:当要在矩阵是填1时让它等于其左上角元素加1。

   b  a  b

c  0  0  0

a  0  1  0

b  1  0  2

a  0  2  0

这样矩阵中的最大元素就是 最长公共子串的长度。

在构造这个二维矩阵的过程中由于得出矩阵的某一行后其上一行就没用了,所以实际上在程序中可以用一维数组来代替这个矩阵。

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */

void getLCString(char* str1, char* str2) 
{
    int i=0, j=0;
    int len1=0, len2=0;
    len1 = strlen(str1);
    len2 = strlen(str2);
    int maxLen = len1 > len2 ? len1 : len2;
    int* max = (int*)malloc(maxLen*sizeof(int));
    memset(max,0,sizeof(max));
    int* maxIndex = (int*)malloc(maxLen*sizeof(int));
    memset(maxIndex,0,sizeof(maxIndex));
    int* c = (int*)malloc(maxLen*sizeof(int));// 记录对角线上的相等值的个数
    memset(c,0,sizeof(c));
    for (i = 0; i < len2; i++) 
    {
        for (j = len1 - 1; j >= 0; j--) 
        {
            if (str2[i] == str1[j]) 
            {
                if ((i == 0) || (j == 0))//防止超出边界 
                    c[j] = 1;
                else
                    c[j] = c[j - 1] + 1;
            } 
            else 
            {
                c[j] = 0;
            }

            if (c[j] > max[0]) 
            { // 如果是大于那暂时只有一个是最长的,而且要把后面的清0;
                max[0] = c[j]; // 记录对角线元素的最大值,之后在遍历时用作提取子串的长度
                maxIndex[0] = j; // 记录对角线元素最大值的位置
                int k = 1;
                for (k = 1; k < maxLen; k++) 
                {
                    max[k] = 0;
                    maxIndex[k] = 0;
                }
            } 
            else if (c[j] == max[0]) 
            { // 有多个是相同长度的子串
                int k = 1;
                for (k = 1; k < maxLen; k++) 
                {
                    if (max[k] == 0) 
                    {
                        max[k] = c[j];
                        maxIndex[k] = j;
                        break; // 在后面加一个就要退出循环了
                    }

                }
            }
        }
    }

    for (j = 0; j < maxLen; j++) 
    {
        if (max[j] > 0) 
        {
            printf("第 %d 个公共子串" ,j+1);
            for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++)
            {
                printf("%c",str1[i]);   
            }               
            printf("\n");
        }
    }
}
void test_1()
{
    int* a = (int*)malloc(10*sizeof(int));
    int i = 0;
    for(i=0;i<10;i++)
    {
        printf("%d\n",a[i]);    
    }
}
int main() 
{
    char* str1 = "123456abcd567";
    char* str2 = "234dddabc45678";
//  char* str1 = "bab";
//  char* str2 = "caba";
    getLCString(str1, str2);
    //test_1();
    return 0;
}

第 1 个公共子串234
第 2 个公共子串abc
第 3 个公共子串456
第 4 个公共子串567

Process exited normally.
Press any key to continue …

2、最长公共子序列

import java.util.Random;

public class LCS {

    public static void main(String[] args) {

        // 随机生成字符串
        // String x = GetRandomStrings(substringLength1);
        // String y = GetRandomStrings(substringLength2);
        String x = "a1b2c3";
        String y = "1a1wbz2c123a1b2c123";
        // 设置字符串长度
        int substringLength1 = x.length();
        int substringLength2 = y.length(); // 具体大小可自行设置

        // 构造二维数组记录子问题x[i]和y[i]的LCS的长度
        int[][] opt = new int[substringLength1 + 1][substringLength2 + 1];

        // 从后向前,动态规划计算所有子问题。也可从前到后。
        for (int i = substringLength1 - 1; i >= 0; i--) {
            for (int j = substringLength2 - 1; j >= 0; j--) {
                if (x.charAt(i) == y.charAt(j))
                    opt[i][j] = opt[i + 1][j + 1] + 1;// 状态转移方程
                else
                    opt[i][j] = Math.max(opt[i + 1][j], opt[i][j + 1]);// 状态转移方程
            }
        }
        System.out.println("substring1:" + x);
        System.out.println("substring2:" + y);
        System.out.print("LCS:");

        int i = 0, j = 0;
        while (i < substringLength1 && j < substringLength2) {
            if (x.charAt(i) == y.charAt(j)) {
                System.out.print(x.charAt(i));
                i++;
                j++;
            } else if (opt[i + 1][j] >= opt[i][j + 1])
                i++;
            else
                j++;
        }
    }

    // 取得定长随机字符串
    public static String GetRandomStrings(int length) {
        StringBuffer buffer = new StringBuffer("abcdefghijklmnopqrstuvwxyz");
        StringBuffer sb = new StringBuffer();
        Random r = new Random();
        int range = buffer.length();
        for (int i = 0; i < length; i++) {
            sb.append(buffer.charAt(r.nextInt(range)));
        }
        return sb.toString();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值