动态规划算法之:最长公共子序列 & 最长公共子串(LCS)

来源:互联网 发布:c语言教学视频哪个好 编辑:程序博客网 时间:2024/05/17 23:58

1、先科普下最长公共子序列 & 最长公共子串的区别:

找两个字符串的最长公共子串,这个子串要求在原字符串中是连续的。而最长公共子序列不要求连续

2、最长公共子串

其实这是一个序贯决策问题,可以用动态规划来求解。我们采用一个二维矩阵来记录中间的结果。这个二维矩阵怎么构造呢?直接举个例子吧:"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

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

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

2.1 代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
publicclass LCString2 {
 
    publicstatic void getLCString(char[] str1, char[] str2) {
        inti, j;
        intlen1, len2;
        len1 = str1.length;
        len2 = str2.length;
        intmaxLen = len1 > len2 ? len1 : len2;
        int[] max = newint[maxLen];
        int[] maxIndex = newint[maxLen];
        int[] c = newint[maxLen];// 记录对角线上的相等值的个数
 
        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; // 记录对角线元素最大值的位置
 
                    for(intk = 1; k < maxLen; k++) {
                        max[k] = 0;
                        maxIndex[k] = 0;
                    }
                }elseif (c[j] == max[0]) { // 有多个是相同长度的子串
                    for(intk = 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) {
                System.out.println("第"+ (j + 1) + "个公共子串:");
                for(i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++)
                    System.out.print(str1[i]);
                System.out.println(" ");
            }
        }
    }
 
    publicstatic void main(String[] args) {
 
        String str1 = newString("123456abcd567");
        String str2 = newString("234dddabc45678");
        // String str1 = new String("aab12345678cde");
        // String str2 = new String("ab1234yb1234567");
        getLCString(str1.toCharArray(), str2.toCharArray());
    }
}
ref:

LCS的java算法---考虑可能有多个相同的最长公共子串

http://blog.csdn.net/rabbitbug/article/details/1740557


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

http://www.cnblogs.com/zhangchaoyang/articles/2012070.html

2.2 其实 awk 写起来也很容易:

?
1
2
echo"123456abcd567
234dddabc45678"|awk -vFS="" 'NR==1{str=$0}NR==2{N=NF;for(n=0;n++<N;){s="";for(t=n;t<=N;t++){s=s""$t;if(index(str,s)){a[n]=t-n;b[n]=s;if(m<=a[n])m=a[n]}else{t=N}}}}END{for(n=0;n++<N;)if(a[n]==m)print b[n]}'

ref:http://bbs.chinaunix.net/thread-4055834-2-1.html

2.3 perl的。。。真心没看懂。。。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/perl
usestrict;
usewarnings;
 
my$str1 ="123456abcd567";
my$str2 = "234dddabc45678";
my$str = $str1. "\n". $str2;
 
my(@substr,@result);
$str=~ /(.+)(?=.*\n.*\1)(*PRUNE)(?{push@substr,$1})(*F)/;
@substr= sort{ length($b) <=> length($a) } @substr;
@result= grep{ length== length$substr[0] } @substr;
print"@result\n";
ref: http://bbs.chinaunix.net/thread-1333575-7-1.html


3、最长公共子序列

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
importjava.util.Random;
 
publicclass LCS {
 
    publicstatic void main(String[] args) {
 
        // 随机生成字符串
        // String x = GetRandomStrings(substringLength1);
        // String y = GetRandomStrings(substringLength2);
        String x = "a1b2c3";
        String y = "1a1wbz2c123a1b2c123";
        // 设置字符串长度
        intsubstringLength1 = x.length();
        intsubstringLength2 = y.length(); // 具体大小可自行设置
 
        // 构造二维数组记录子问题x[i]和y[i]的LCS的长度
        int[][] opt = newint[substringLength1 + 1][substringLength2 + 1];
 
        // 从后向前,动态规划计算所有子问题。也可从前到后。
        for(inti = substringLength1 - 1; i >= 0; i--) {
            for(intj = 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:");
 
        inti = 0, j = 0;
        while(i < substringLength1 && j < substringLength2) {
            if(x.charAt(i) == y.charAt(j)) {
                System.out.print(x.charAt(i));
                i++;
                j++;
            }elseif (opt[i + 1][j] >= opt[i][j + 1])
                i++;
            else
                j++;
        }
    }
 
    // 取得定长随机字符串
    publicstatic String GetRandomStrings(intlength) {
        StringBuffer buffer = newStringBuffer("abcdefghijklmnopqrstuvwxyz");
        StringBuffer sb = newStringBuffer();
        Random r = newRandom();
        intrange = buffer.length();
        for(inti = 0; i < length; i++) {
            sb.append(buffer.charAt(r.nextInt(range)));
        }
        returnsb.toString();
    }
}
REF:

字符串最大公共子序列以及最大公共子串问题

http://gongqi.iteye.com/blog/1517447

动态规划算法解最长公共子序列LCS问题

http://blog.csdn.net/v_JULY_v/article/details/6110269

转载网址:http://my.oschina.net/leejun2005/blog/117167

0 0