[学習記録]AtCoder EDP F-LCS

問題

問題文

文字列 s および t が与えられます。 s の部分列かつ t の部分列であるような文字列のうち、最長のものをひとつ求めてください。

制約

  • s および t は英小文字からなる文字列である。

  • 1≤∣s∣,∣t∣≤3000

解法

s, tを1文字ずつ取り出し、処理を行う。dpには最大文字数を記録する。

  • マッチした  → 1文字前の最大長 + 1(マッチした文字)

  • マッチしない → 1文字前の最大長(s) と 1文字前の最大長(t)の最大

for i := 1; i <= s_len; i++ {
	dp[i] = make([]int, t_len+1)
	s := s_sli[i-1]

	for j := 1; j <= t_len; j++ {
		t := t_sli[j-1]
		if s == t {
			dp[i][j] = dp[i-1][j-1] + 1
		} else {
			dp[i][j] = Max(dp[i-1][j], dp[i][j-1])
		}
	}
}

dpができたら、ここから文字列を復元する必要がある。復元したら、その配列を1文字ずつ出力したものが回答になる。

// 最大文字数を取得し、それを格納するスライスを作る
len := dp[s_len][t_len]
ans := make([]string, len)

for i, j := s_len, t_len; len > 0; {
	if s_sli[i-1] == t_sli[j-1] {
        // 文字が同じ場合はそれを出力用に保持(len 文字目)
		ans[len-1] = s_sli[i-1]

        // s, t ともにその文字を使うことはできないので、マイナス
		i--
		j--
		len--
	} else if dp[i-1][j] == dp[i][j]-1 {
     // 文字が違う = どちらかの文字は使わない => dpの値を確認
     // dp[i-1][j]が減る = dp[i][j-1]の値は同じ => j をマイナス
     // (dpの値はマッチした時のみ +1されており、それ以外はないため)
		j--
	} else {
		i--
	}
}

for _, v := range ans {
	fmt.Print(v)
}
fmt.Println()
// 参考
//   a b r a c a d a b r a
// a 1 1 1 1 1 1 1 1 1 1 1
// v 1 1 1 1 1 1 1 1 1 1 1
// a 1 1 1 2 2 2 2 2 2 2 2
// d 1 1 1 2 2 2 3 3 3 3 3
// a 1 1 1 2 2 3 3 3 3 3 3
// k 1 1 1 2 2 3 3 3 3 3 3
// e 1 1 1 2 2 3 3 3 3 3 3
// d 1 1 1 2 2 3 4 4 4 4 4
// a 1 1 1 2 2 3 4 5 5 5 5
// v 1 1 1 2 2 3 4 5 5 5 5
// r 1 1 2 2 2 3 4 5 5 6 6
// a 1 1 2 3 3 3 4 5 6 6 7
//   a b r a c a d a b r a

コード(AC)

func run() {
	s_sli := strings.Split(read(), "")
	t_sli := strings.Split(read(), "")

	dp := make([][]int, len(s_sli)+1)
	s_len := len(s_sli)
	t_len := len(t_sli)
	dp[0] = make([]int, t_len+1)

	for i := 1; i <= s_len; i++ {
		dp[i] = make([]int, t_len+1)
		s := s_sli[i-1]

		for j := 1; j <= t_len; j++ {
			t := t_sli[j-1]
			if s == t {
				dp[i][j] = dp[i-1][j-1] + 1
			} else {
				dp[i][j] = Max(dp[i-1][j], dp[i][j-1])
			}
		}
	}

	len := dp[s_len][t_len]
	ans := make([]string, len)
	for i, j := s_len, t_len; len > 0; {
		if s_sli[i-1] == t_sli[j-1] {
			ans[len-1] = s_sli[i-1]
			i--
			j--
			len--
		} else if dp[i-1][j] == dp[i][j]-1 {
			j--
		} else {
			i--
		}
	}

	for _, v := range ans {
		fmt.Print(v)
	}
	fmt.Println()
}


この記事が気に入ったらサポートをしてみませんか?