AOJ1316 The Sorcerer's Donut
問題リンク The Sorcerer's Donut
- 概要
H*Wに文字が並んでいる。ある文字からはじめて、周囲8方向へ向きを決めて読み続けることができる。表の外へはみ出すとき、上辺へはみ出すならば下の辺へ、右辺へはみ出すならば左辺へ、というように読み続ける。
magic spellとは、2文字以上の文字列で、2回以上出現するもののことである。ただし、始まりの文字が異なり、互いにオーバラップしていないようなものである。H*Wの表中で最も長いmagic spellを答えよ。そのようなものが複数ある場合辞書順で小さいものを答えよ。
3 <= H <= 10
3 <= W <= 20
- 解法
点i, jから向きdに読み始め、点i, jの文字を先頭とする部分文字列を全て列挙していきます。
set[i][j]はアルファベットiから始まり、長さjの文字列を蓄える文字列集合です。
部分文字列を1文字ずつ伸ばしていって、もし、それがsetに含まれていたらそれは異なる箇所でオーバラップせず出現した文字列なのでmagic spellです。これを全ての点を始点に、全方向試していきます。
- ソース
import java.util.HashSet; import java.util.Scanner; import java.util.Set; //The Sorcerer's Donut public class AOJ1316 { @SuppressWarnings("unchecked") void run(){ int[][] move = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}}; Scanner sc = new Scanner(System.in); for(;;){ int h = sc.nextInt(), w = sc.nextInt(); if((h|w)==0)break; char[][] m = new char[h][w]; for(int i=0;i<h;i++)m[i]=sc.next().toCharArray(); Set<String>[][] set = new HashSet[26][201]; for(int i=0;i<26;i++)for(int j=0;j<=200;j++)set[i][j] = new HashSet<String>(); String res = ""; for(int i=0;i<h;i++)for(int j=0;j<w;j++){ for(int k=0;k<8;k++){ StringBuilder sb = new StringBuilder(m[i][j]+""); int pi = (i+move[k][0]+h)%h, pj = (j+move[k][1]+w)%w; while(pi!=i||pj!=j){ sb.append(m[pi][pj]); String r = sb.toString(); if(set[m[i][j]-'A'][r.length()].contains(r)){ if(res.length()<r.length())res = r; else if(res.length()==r.length()&&r.compareTo(res)<0)res = r; } else set[m[i][j]-'A'][r.length()].add(r); pi = (pi+move[k][0]+h)%h; pj = (pj+move[k][1]+w)%w; } } } System.out.println("".equals(res)?"0":res); } } public static void main(String[] args) { new AOJ1316().run(); } }