標準9251:LCS[LCS,DP]

16028 ワード

9251号:LCS
package string

import java.io.*

fun main():Unit = with(BufferedReader(InputStreamReader(System.`in`))){
    val str1 = readLine().trim()
    val str2 = readLine().trim()

    val array = Array<Array<Int>>(str2.length+1){Array<Int>(str1.length+1){0}}

    for (i in 1 until str2.length+1){
        for (j in 1 until str1.length+1){
            if (str1[j-1] == str2[i-1]){
                array[i][j] = array[i-1][j-1]+1
            }else{
                array[i][j] = array[i-1][j].coerceAtLeast(array[i][j-1])
            }
        }
    }
    println(array.last().last())
}
説明:
  • 文字列のLongestCommon Subescence(最長共通部分数列)を探す問題です.
  • 2つの文字列
  • が与えられると、すべての部分数列の中で最も長い1つが検索される.
  • ACAYKPとCAPCAKのLCSはACAKです.
  • コードのarrayは、2つの文字列の共通部分の個数を格納する配列である.array[i][j]を表す
    str 2の0~(i−1)サブストリングとstr 1の0~(j−1)サブストリングのLCS長さ.
  • arrayの行値を2番目の入力(文字列の長さ+1)に設定します.
  • arrayのcol値を最初の入力(文字列長+1)に設定します.
  • +1を行ったのは、次のサイクルの点火式でIndexOutOfBoundExceptionが出ないようにするためです.
  • 点火式の説明
    if (str1[j-1] == str2[i-1]){
    	array[i][j] = array[i-1][j-1]+1
    }
  • の現在のインデックスの文字が同じであれば、LCS長に1文字が追加されたことを示し、上述した点化方式を形成する.
  • 点火式の説明
    else{
    	array[i][j] = array[i-1][j].coerceAtLeast(array[i][j-1])
    }
  • 現在のインデックスの文字が異なります.この場合、従来のLCSが維持されるため、上記点火式が形成される.
  • 時間の複雑さ
  • O(N2)O(N^2)O(N2) → N = string length
  • LongestCommon Subsequence出力法
  • 点火式によれば、配列の最後の行、最後のcolは配列の最大値を格納する.
  • このインデックスから、配列の上、左の値をチェックし、自分と同じ数字があればrow、colの値を更新します.
    if (array[row-1][col] == array[row][col]){ row = row-1 }
    else if (array[row][col-1] == array[row][col]){col = col -1}
  • 自身と同じ値が存在しない場合は、row、col値を左上隅で更新します.
  • および更新前に、インデックスに対応する文字が任意の空間に格納される.
    else{
    	lcs.append(str1[col-1])
    	row = row - 1
    	col = col - 1
    }
  • ビット目のプロシージャを配列値0に繰り返します.
    var row = array.size-1
    var col = array[0].size-1
    var lcs = StringBuilder()
    while (array[row][col]!=0){
            //[row-1][col] or [row][col-1]에 [row][col]값과 같은 값이 있는지 찾기
            //있다면 row, col 값 update
            //없다면 row = row - 1 and col = col - 1 로 update -> 문자열 값 넣어주기
    	if (array[row-1][col] == array[row][col]){ row = row-1 }
    	else if (array[row][col-1] == array[row][col]){col = col -1}
    	else{
    		lcs.append(str1[col-1])
    		row = row - 1
    		col = col - 1
    	}
    }
    println(lcs.toString().reversed())
  • 最後に保存した文字列の逆順文字列を出力するとLCSが出力されます.