问题:

如果两个单词很像,其实是他们有更长的公共子序列。求以下几组单词的最长公共子序列的长度

1.fish和fosh
2.recommend和reccommend
3.fish和vista

思路:

与最长公共子串很类似,但子序列遇到不同的字符时不能将相同字符组计数清零。需要连续计数,此时,需要将当前位置左侧或上侧的最大值填入到当前位置。

解答:

php:

// 找出两个单词的最长公共子序列
function findLongestSubSeq($word1, $word2)
{
    $len1 = strlen($word1);
    $len2 = strlen($word2);
    $cell = array();
    for ($i = 0; $i < $len1; $i++) {
        for ($j = 0; $j < $len2; $j++) {

            // 如果两个字符相同,则取其左上角的数值+1
            if ($word1[$i] == $word2[$j]) {
                if ($i > 0 && $j > 0) {
                    $cell[$i][$j] = $cell[$i - 1][$j - 1] + 1;
                } else {
                    $cell[$i][$j] = 1;
                }
            } else {
                if ($i > 0 || $j > 0) {
                    $v1 = 0;
                    if ($i > 0){
                        $v1 = $cell[$i - 1][$j];
                    }

                    $v2 = 0;
                    if ($j > 0){
                        $v2 = $cell[$i][$j - 1];
                    }
                    $cell[$i][$j] = max($v1, $v2);
                }else{
                    $cell[$i][$j] = 0;
                }
            }
        }
    }
    printCell($word1, $word2, $cell);
    $maxLength = findMaxValue($cell);
    echo $maxLength . "\n";
}

// 找出值最大的单元格
function findMaxValue($cell)
{
    $max = 0;
    foreach ($cell as $rows) {
        foreach ($rows as $item) {
            if ($item > $max) {
                $max = $item;
            }
        }
    }
    return $max;
}

function printCell($word1, $word2, $cell)
{
    $len1 = strlen($word1);
    $len2 = strlen($word2);
    echo "  ";
    for ($i = 0; $i < $len2; $i++) {
        echo $word2[$i] . " ";
    }
    echo "\n";
    for ($i = 0; $i < $len1; $i++) {
        echo $word1[$i] . " ";
        echo implode(' ', $cell[$i]) . "\n";
    }

}

findLongestSubSeq("fish", "fosh");
findLongestSubSeq("recommend", "reccommend");
findLongestSubSeq("hish", "vista");

输出:

  f o s h 
f 1 1 1 1
i 1 1 1 1
s 1 1 2 2
h 1 1 2 3
3
  r e c c o m m e n d 
r 1 1 1 1 1 1 1 1 1 1
e 1 2 2 2 2 2 2 2 2 2
c 1 2 3 3 3 3 3 3 3 3
o 1 2 3 3 4 4 4 4 4 4
m 1 2 3 3 4 5 5 5 5 5
m 1 2 3 3 4 5 6 6 6 6
e 1 2 3 3 4 5 6 7 7 7
n 1 2 3 3 4 5 6 7 8 8
d 1 2 3 3 4 5 6 7 8 9
9
  v i s t a 
h 0 0 0 0 0
i 0 1 1 1 1
s 0 1 2 2 2
h 0 1 2 2 2
2

golang:

package main

import (
    "fmt"
    "math"
)

func main() {
    findLongestSubSeq("fish", "fosh")
    findLongestSubSeq("recommend", "reccommend")
    findLongestSubSeq("fish", "vista")
}

func findLongestSubSeq(word1, word2 string) {
    len1 := len(word1)
    len2 := len(word2)
    cell := make([][]int, len1)
    for i := 0; i < len1; i++ {
        cell[i] = make([]int, len2)
        for j := 0; j < len2; j++ {
            if word1[i] == word2[j] {
                if i > 0 && j > 0 {
                    cell[i][j] = cell[i-1][j-1] + 1
                }else{
                    cell[i][j] = 1
                }
            } else {
                v1 := 0
                if i > 0 {
                    v1 = cell[i-1][j]
                }

                v2 := 0
                if j > 0 {
                    v2 = cell[i][j-1]
                }
                cell[i][j] = int(math.Max(float64(v1), float64(v2)))
            }
        }
    }
    val := findMaxValue(cell)
    fmt.Println(val)
}

func findMaxValue(cell [][]int) int {
    max := 0
    for _, rows := range cell {
        for _, item := range rows {
            if item > max {
                max = item
            }
        }
    }
    return max
}

输出:

3
9
2