HaskellでLCS(最長共通部分列問題)

LCSは以前C++

最長共通部分列(LCS)
http://d.hatena.ne.jp/g940425/20110210/1297296036

書きましたが、ちょっと今回Haskellで書いてみようと思います。

動機は、Haskellでは代入ができないのでループを回して二次元配列を更新、という従来の手続き型DPが書けないとのことなので、純粋関数型言語のDPを練習をしようと思った次第です。

意外と簡単に書けるものですね。

ちなみに、下のコードを書くにあたり、以下のサイトを参考にしました。

Haskell の多次元配列(tnomuraのブログ)」
http://tnomura9.exblog.jp/10407594/

Haskell動的計画法を書くための3つの方針(tosの日記)」
http://d.hatena.ne.jp/toslunar/20100408/1270719176


2変数関数fがDPの漸化式で、これをDP配列の値であると定義してやれば、あとはリスト内包表記で書けばインデクスが回ってDP配列を逐次的に計算してくれます。

リスト内包表記はうまく使うと便利ですね。

import Array

lcs l1 l2 =
    let dp = array ((0,0),((length l1),(length l2))) [((i,j),f i j)|i<-[0..(length l1)],j<-[0..(length l2)]]
             where
               f i j = if (i==0||j==0) then
                           0 
                       else if l1!!(i-1)==l2!!(j-1) then 
                                (dp!(i-1,j-1))+1 
                            else  (max (dp!(i-1,j)) (dp!(i,j-1)))
    in
      dp!((length l1),(length l2))

main = do str1 <- getLine
          str2 <- getLine
          putStrLn (show (lcs str1 str2))


C++に比べて短く書けていいですねー。

トレースバックはまだ実装してませんが、後日実装すると思いますので、そのときはまた書きます。

ちょっとHaskell練習中なのでこれから記事にしていこうかと思います。