Problem:
共通部分文字列

問題

2 個の文字列が与えられたとき, 両方の文字列に含まれる文字列のうち最も長いものを探し, その長さを答えるプログラムを作成せよ.

ここで, 文字列 s が文字列 t に含まれるとは, t の中に s が連続して現れることをいう. 空文字列, すなわち長さ 0 の文字列は, どんな文字列にも含まれる. 例えば, 文字列 ABRACADABRA には次の文字列が含まれる: ABRA, RAC, D, ACADABRA, ABRACADABRA,空文字列など. 一方, 文字列 ABRACADABRA には次の文字列は含まれない: ABRC, RAA,BA, K など.

例

例 1: 文字列として ABRACADABRA と ECADADABRBCRDARA が与えられた場合, 両方に含まれる文字列には CA や CADA や ADABR や空文字列などがある. そのうち最も長いのは ADABR であり, その長さは 5 である. 2 個の文字列の中に含まれる ADABR
の位置を以下に示す.

ABRACADABRA

ECADADABRBCRDARA

例 2: 文字列として UPWJCIRUCAXIIRGL と SBQNYBSBZDFNEV が与えられた場合, 両方に含まれる文字列は空文字列のみであり, その長さは 0 である.

入力

入力は複数のデータセットからなる．各データセットは以下の形式で与えられる．

入力は 2 行からなり, 1 行目に 1 個目の文字列が, 2 行目に 2 個目の文字列が与えられる. 文字列は英大文字からなり, 各々の文字列の長さは 1 以上 4000 以下である.

採点用データのうち, 配点の 30% 分については, 各々の文字列の長さは 1 以上 50以下である.

入力の終わりは EOF で示される. データセットの数は 10 を超えない．

出力

データセットごとに与えられた 2 個の文字列の両方に含まれる文字列のうち最も長いものの長さを 1 行に出力する.

入出力例

入力例

ABRACADABRA
ECADADABRBCRDARA
UPWJCIRUCAXIIRGL
SBQNYBSBZDFNEV

出力例

5
0

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Time Limit Exceeded)

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class Main {

	/**
	 * @param args 
	 */
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		L:while(sc.hasNext()){
			String ss = sc.next();
			String tt = sc.next();
			int[]s = new int[ss.length()];
			int[]t = new int[tt.length()];
			for(int i=0;i<s.length;i++){
				s[i] = ss.charAt(i);
			}
			for(int i=0;i<t.length;i++){
				t[i] = tt.charAt(i);
			}
			SuffixArray sa = new SuffixArray(s);
			int ans=0;
			for(int len=t.length;len>=0;len--){
				int[] sub = new int[len];
				for(int i=0;i<t.length;i++){
					int j = i+len;
					if(j>t.length)break;
					for(int k=i;k<i+len;k++){
						sub[k-i]=t[k];
					}
					if(sa.contain(sub)){
						System.out.println(len);
						continue L;
					}
				}
			}
			System.out.println(ans);
		}
	}
	static class SuffixArray {

		int n,k=1;
		Integer[] sa,rank,tmp;
		int[] s;
		int[][] S;
		public static void main(String[] args) {
			new SuffixArray(new int[]{3,4,1,2,5});
		}
		SuffixArray(int[] _s) {
			s=_s;
			n = s.length;
			S = new int[n+1][];
			S[n]=new int[0];
			for(int i=n-1;i>=0;i--){
				S[i]=new int[S[i+1].length+1];
				for(int j=S[i+1].length-1;j>=0;j--){
					S[i][j+1] = S[i+1][j];	
				}
				S[i][0] = s[i];
			}
			rank=new Integer[n+1];
			sa=new Integer[n+1];
			tmp=new Integer[n+1];
			for(int i=0;i<n;i++){
				sa[i]=i;
				rank[i]=s[i];
			}
			sa[n]=n;
			rank[n]=-1;

			for(k=1;k<=n;k<<=1){
				Arrays.sort(sa,new Comparator<Integer>(){
					@Override
					public int compare(Integer o1, Integer o2) {
						int r1=rank[o1];
						int r2=rank[o2];
						if(r1!=r2)return -(r1-r2);
						int r11=o1+k <= n ? rank[o1+k] : -1;
						int r22=o2+k <= n ? rank[o2+k] : -1;

						return -(r11-r22);
					}
				});
				tmp[sa[0]]=0;
				for(int i=1;i<=n;i++){
					tmp[sa[i]]=tmp[sa[i-1]]+ (compare(sa[i-1],sa[i])>0?1:0);
				}
				for(int i=0;i<=n;i++){
					rank[i]=tmp[i];
				}
			}
		}

		/*
		 * s.contains(t)的な
		 */
		boolean contain(int[] t){
			return contain(s,sa,t);
		}

		private boolean contain(int[]s,Integer[] sa,int[] t){
			int a=0,b=s.length;
			while(b-a>1){
				int c = (a+b)/2;
				//				String sub = s.substring(sa[c], Math.min(sa[c]+t.length, s.length));
				if(compare(s,sa[c],Math.min(sa[c]+t.length, s.length),t)<0){
					a=c;
				}
				else{
					b=c;
				}
			}
			return compare(s,sa[b], Math.min(sa[b]+t.length, s.length),t)==0;
		}

		/*
		 * s.substring(i1,i2).compareTo(t) 的な
		 */
		private int compare(int[]s,int i1,int i2,int[] t){
			for(int i=i1;i<i2;i++){
				if(i-i1>=t.length){
					return 1;
				}
				if(s[i]!=t[i-i1]){
					return s[i]-t[i-i1];
				}
			}
			return (i2-i1)-t.length;
		}

		private int compare(Integer o1, Integer o2) {
			int r1=rank[o1];
			int r2=rank[o2];
			if(r1!=r2)return (r1-r2);
			int r11=o1+k <= n ? rank[o1+k] : -1;
			int r22=o2+k <= n ? rank[o2+k] : -1;

			return (r11-r22);
		}

	}

}