challenge いちばん長いしりとり

単語のリストを読み込んで、そのリストにある単語で「しりとり」をします。
一番長くしりとりを続けるためのプログラムを書いてください。
また、単語数に対して、計算量がどのように増えていくかも考えて下さい。

なお、単語リストの一例として
http://www.ais.riec.tohoku.ac.jp/lab/wordlist/index-j.htmlで公開されている
http://www.ais.riec.tohoku.ac.jp/lab/wordlist/fam55_40.txtがあります。

ただし、
・一度使った単語は使わないこと(リストに重複がある可能性は考えなくてよい)
・「ん」で終わる単語を使用するか、リスト内にしりとりを続けられる単語がなくなったときに、しりとりは終了する
・一番最初は、好きな単語から初めてもよい
・「一番長くしりとりを続ける」とは、しりとりが終了するまでに使用する単語数が最大になるよう、しりとりの単語を選ぶことをいう

Posted feedbacks - Other

非常に単純な実装。 およそO(N!)のため、Nは100前後が限界です。

% longtail <longtail.data
 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
64
65
66
67
68
69
implement Longtail;

include "sys.m";
    sys: Sys;
include "draw.m";
include "bufio.m";
    bufio: Bufio;
    Iobuf: import bufio;
include "string.m";
    str: String;

Longtail: module
{
    init: fn(ctxt: ref Draw->Context, argv: list of string);
};

Word: adt
{
    s: string;
    used: int;
};

MAXRUNE: con 16rffff;

tab := array[MAXRUNE] of list of ref Word;

init(nil: ref Draw->Context, nil: list of string)
{
    sys = load Sys Sys->PATH;
    bufio = load Bufio Bufio->PATH;
    str = load String String->PATH;

    install(sys->fildes(0));

    wstart := ref Word("シリトリ", 0);
    result := chain(wstart);
    for(p := result; p != nil; p = tl p)
        sys->print("%s\n-> ", (hd p).s);
    sys->print("END\n");
}

install(fd: ref Sys->FD)
{
    fin := bufio->fopen(fd, bufio->OREAD);
    while((t := fin.gett(" \t\n")) != nil){
        (t, nil) = str->splitl(t, " \t\n");
        if(t == "")
            continue;
        tab[t[0]] = ref Word(t, 0) :: tab[t[0]];
    }
}

chain(w: ref Word): list of ref Word
{
    longest: list of ref Word;

    w.used = 1;
    lastc := w.s[len w.s - 1];
    for(p := tab[lastc]; p != nil; p = tl p){
        w1 := hd p;
        if(w1.used)
            continue;
        t := chain(w1);
        if(len t > len longest)
            longest = t;
    }
    w.used = 0;
    return w :: longest;
}

何度もすみません、また誤りがあったので訂正します。

よく確認してから投稿します。お目汚し失礼致しました。

1
2
3
4
5
6
7
8
9
67:        for(j = 0; j < n; i++)
68:        {
69:            check[i] = 1;
70:        }
↓
67:        for(j = 0; j < n; j++)
68:        {
69:            check[j] = 1;
70:        }

F#で書いてみました。 単語の最初と最後の文字の組み合わせで、2次元配列を作り、それをもとにして、全探索しています。計算量はO(N!)だと思います。 100までで、22個 カザアナ-> ナニゴト-> トリモノ-> ノリニゲ-> ゲレツサ-> サンバシ-> シールド-> ドウナガ-> ガイユウ-> ウラガネ-> ネンブツ-> ツジツマ-> マヤカシ-> シタヅミ-> ミズヒキ-> キヤクアシ-> シヨウワル-> ルイベツ-> ツユザム-> ムスビメ-> メイフク-> クタビレ が最大のもののひとつです。 130で32個ですが、これで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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
open System.IO

//小文字を大文字に変換
let ConvH (str:string) =
    str.Replace("ァ","ア").Replace("ィ","イ").Replace("ゥ","ウ").Replace("ェ","エ").Replace("ォ","オ")
        .Replace("ヵ","カ").Replace("ヶ","ケ").Replace("ッ","ツ").Replace("ャ","ヤ").Replace("ュ","ユ")
        .Replace("ョ","ヨ").Replace("ヮ","ワ")
             
//ファイルの文字列を大文字化してリストに収める
let wordList = [
                    use fileReader = new StreamReader("s:\word2.text") 
                    while not fileReader.EndOfStream do
                        let line = fileReader.ReadLine()
                        let strs = line.Split( [| '\t' |])
                        for st in strs do
                            if st <> "" then
                                yield ConvH (st)
                 ]

let IcharSet = List.fold (fun (chset:char Set) (str:string) -> Set.add str.[0] chset) (Set.Empty)  wordList    
let ITcharSet = List.fold (fun (chset:char Set) (str:string) -> Set.add (str.[str.Length - 1]) chset) IcharSet  wordList    
let UsedCharList = Set.to_list ITcharSet //先頭か末尾で使われている文字のリスト

let KanaLen = List.length  UsedCharList

//カナのindexを返す
let posOfKana (ch : char) =
    List.findIndex (fun x -> x = ch) UsedCharList
   
//対応表用の配列 サイズ KanaLen * KanaLen
let respT = [| for i in 0 .. (KanaLen - 1) do
                    yield (Array.create KanaLen 0) |]

for s in wordList do
    let topStr = s.[0]
    let endStr = s.[s.Length - 1]
    let topStrIndex = posOfKana topStr
    let endStrIndex = posOfKana endStr
    respT.[topStrIndex].[endStrIndex] <- respT.[topStrIndex].[endStrIndex] + 1  

let tempWordIndexArr = Array.create wordList.Length 0
let deepestWordIndexArr = Array.create wordList.Length 0                

// topIndex..何で始まるのから調べるか 
let rec search depth deepest topIndex  (arr :int [] []) (indexArr :int []) =
    for i in  0 ..(KanaLen - 1) do
        if arr.[topIndex].[i] > 0 then
            
            indexArr.[depth] <- topIndex
            indexArr.[depth+1] <- i
            arr.[topIndex].[i] <- arr.[topIndex].[i] - 1
            
            if depth + 1 > !deepest then
               deepest := depth + 1
               for i in 0 .. !deepest do
                    deepestWordIndexArr.[i] <- indexArr.[i]
          
            search (depth + 1) deepest i arr indexArr
            
            arr.[topIndex].[i] <- arr.[topIndex].[i] + 1

let maxDepth = ref 0

for i in 0 .. (KanaLen - 1) do
    if Array.sum respT.[i] > 0 then
        search 0 maxDepth i respT tempWordIndexArr

printfn "最大連結個数: %A" !maxDepth   

//結果表示用の補助関数
//index iで始まりjで終わる単語をリストから抜き出して、その単語とその単語を除いたリストを返す
let findAndPop (i,j) lst =
    let rec sub (passedLst : string list)  (yetLst : string list) =
       match yetLst with
       | [] -> failwith "dont find"
       | h :: tl when posOfKana h.[0] = i && posOfKana h.[h.Length - 1] = j 
                -> (h,passedLst @ tl)
       | h :: tl -> sub (passedLst @ [h]) tl
    sub [] lst            

//結果表示用
let rec dispResult lst i =
    if i = !maxDepth then
        ()
    else 
        let (word,remLst) = findAndPop ( deepestWordIndexArr.[i],deepestWordIndexArr.[i+1]) lst
        printf "-> %s" word
        dispResult remLst (i + 1)

dispResult wordList 0

Index

Feed

Other

Link

Pathtraq

loading...