/* Matcher.java  V1.1
 * Created by Jeremy Naman
 * July 11, 2003
 */

import java.util.*;
import java.io.*;


public class Matcher4 {

    private static final boolean DEBUG = false;

    public static void main(String[] args) {
	
	if(args.length != 1) {
	    System.out.println("Usage: java Matcher <filename>");
	    System.out.println("File format:");
	    System.out.println("  Line 1 - \"tr\" string");
	    System.out.println("  Line 2 - \"ref\" string");
	    System.exit(0);
	}
	
	// Read the file
	String[] input = parseFile(args[0]);
	
	StringTokenizer tr = new StringTokenizer( input[0], " " );
	StringTokenizer ref = new StringTokenizer( input[1], " " );

	ArrayList tokenized_tr = new ArrayList();
	ArrayList tokenized_ref = new ArrayList();
	
	// build tokenized_tr array
	for(int i=0; tr.hasMoreTokens(); i++) {
	    tokenized_tr.add( tr.nextToken() );
	}
	
	// build tokenized_ref array
	for(int i=0; ref.hasMoreTokens(); i++) {
	    tokenized_ref.add( ref.nextToken() );
	}
	
	ArrayList matches = match( tokenized_tr, tokenized_ref );
	int bestscore = 2147483647; // max int value
	for(int i=0; i < matches.size(); i++) {
	    ArrayList thismatch = (ArrayList)matches.get(i);
	    int thisscore = getScore((int[])thismatch.get(1));
	    if( thisscore < bestscore ) bestscore = thisscore;
	    thismatch.add(new Integer(thisscore));
	}

	//	Collections.sort( matches, new MatchSorter() );
	
	if(matches.size() > 0) {
	    System.out.println(((int[])((ArrayList)matches.get(0)).get(1)).length);
	    System.out.println( bestscore );
	    /*
	    for(int i=0; i < matches.size(); i++) {
		ArrayList thismatch = (ArrayList)matches.get(i);
		System.out.println( (String)thismatch.get(0) + "  " + (Integer)thismatch.get(2) );
	    }
	    */
	}
	else {
	    System.out.println("0");
	}

	try{
	} catch(Exception e) {
	    System.out.println(e);
	    System.exit(0);
	}
    }

    /*
    private static class MatchSorter implements Comparator {
	public int compare(Object o1, Object o2) {
	    Integer a = (Integer)((ArrayList)o1).get(2);
	    Integer b = (Integer)((ArrayList)o2).get(2);
	    return (a).compareTo(b);
	}
    }
    */
    
    public static ArrayList match(ArrayList tr, ArrayList ref) {
	ArrayList ret = new ArrayList(); // the return value

	// Build relevant tr lists
	ArrayList sorted_tr = new ArrayList();
	ArrayList reduced_tr = new ArrayList();
	ArrayList tr_indices = new ArrayList();
	for(int i=0; i < tr.size(); i++) {
	    sorted_tr.add(new String((String)tr.get(i)));
	    // build the "reduced" list-base while we're here
	    reduced_tr.add(new String((String)tr.get(i)));
	    // put the indices in now; we'll use them later
	    tr_indices.add(new Integer(i));
	}
	// Sort tr
	Collections.sort(sorted_tr);

	// Build relevant ref lists
	ArrayList sorted_ref = new ArrayList();
	ArrayList reduced_ref = new ArrayList();
	ArrayList ref_indices = new ArrayList();
	for(int i=0; i < ref.size(); i++) {
	    sorted_ref.add(new String((String)ref.get(i)));
	    // build reduced at same time
	    reduced_ref.add(new String((String)ref.get(i)));
	    // put the indices in now; we'll use them later
	    ref_indices.add(new Integer(i));
	}
	// Sort ref
	Collections.sort(sorted_ref);

	
	// Find common set with comparison (and build word count arrays)
	ArrayList common = new ArrayList();
	ArrayList wordcount_tr = new ArrayList();
	ArrayList wordcount_ref = new ArrayList();
	while( !sorted_tr.isEmpty() && !sorted_ref.isEmpty() ) {
	    int comp = ((String)sorted_tr.get(0)).compareTo((String)sorted_ref.get(0));
	    if( comp < 0 ) {
		String temp = (String)sorted_tr.remove(0);
		int index = common.indexOf(temp);
		if( index != -1 ) {
		    int c = ((Integer)wordcount_tr.get(index)).intValue();
		    wordcount_tr.set( index, new Integer(c+1) );
		}
	    }
	    else if( comp == 0 ) {
		String temp = (String)sorted_tr.remove(0);
		sorted_ref.remove(0); // doesn't matter which one
		int index = common.indexOf(temp);
		if( index != -1 ) {
		    int c1 = ((Integer)wordcount_tr.get(index)).intValue();
		    int c2 = ((Integer)wordcount_ref.get(index)).intValue();
		    wordcount_tr.set( index, new Integer(c1+1) );
		    wordcount_ref.set( index, new Integer(c2+1) );
		}
		else {
		    // place this new word in the common and wordcount lists
		    common.add( new String(temp) );
		    wordcount_tr.add( new Integer(1) );
		    wordcount_ref.add( new Integer(1) );
		}
	    }
	    else if( comp > 0 ) {
		String temp = (String)sorted_ref.remove(0);
		int index = common.indexOf(temp);
		if( index != -1 ) {
		    int c = ((Integer)wordcount_ref.get(index)).intValue();
		    wordcount_ref.set( index, new Integer(c+1) );
		}
	    }
	}
	
	
	// ********************************************************
	if(DEBUG) {
	    System.out.println("common: " + common);
	    System.out.println("wordcount_tr: " + wordcount_tr);
	    System.out.println("wordcount_ref: " + wordcount_ref);
	} // OK! :)
	// ********************************************************
	
	// If none in common, return here
	if( common.isEmpty() ) {
	    return ret;
	}
	
	// reduce tr
	for(int i=0; i < reduced_tr.size(); i++) {
	    if(!common.contains(((String)reduced_tr.get(i)))) {
		reduced_tr.remove(i);
		tr_indices.remove(i); // remove the corresponding index value
		i--;
	    }
	}
	// reduce ref
	for(int i=0; i < reduced_ref.size(); i++) {
	    if(!common.contains(((String)reduced_ref.get(i)))) {
		reduced_ref.remove(i);
		ref_indices.remove(i); // remove the corresponding index value
		i--;
	    }
	}
	
	// *********************
	if(DEBUG) {
	    System.out.println("reduced_tr: " + reduced_tr);
	    System.out.println("reduced_ref: " + reduced_ref);
	} // OK
	// *********************
	
	// build all orderings for tr
	ArrayList tr_orderings = getOrderings( reduced_tr, tr_indices, common, wordcount_tr, wordcount_ref );

	// build all orderings for ref
 	ArrayList ref_orderings = getOrderings( reduced_ref, ref_indices, common, wordcount_ref, wordcount_tr );
	
	// GARBAGE COLLECT
	System.gc();
	
	// create matchings, decide indices for the tr lines
	for(int a=0; a < tr_orderings.size(); a++) {
	    for(int b=0; b < ref_orderings.size(); b++) {

		ArrayList thistr = (ArrayList)tr_orderings.get(a);
		ArrayList thisref = (ArrayList)ref_orderings.get(b); // the ref indices
		// these guys match up one-to-one
		
		String[] stringtr = new String[thistr.size()];
		for(int i=0; i < stringtr.length; i++) {
		    stringtr[i] = (String)tr.get(((Integer)thistr.get(i)).intValue());
		}
		
		ArrayList stringref = new ArrayList();
		for(int i=0; i < thisref.size(); i++) {
		    stringref.add( (String)ref.get(((Integer)thisref.get(i)).intValue()) );
		}
		
		int[] tr_chosen_indices = new int[thistr.size()];
		for(int i=0; i < stringtr.length; i++) {
		    int index_1 = stringref.indexOf(stringtr[i]);
		    int index_2 = ((Integer)thisref.get(index_1)).intValue();
		    tr_chosen_indices[i] = index_2;
		    stringref.set(index_1, null);
		}
		
		/*
		  String alignment = "(";
		  for(int i=0; i < stringtr.length; i++) {
		  alignment += " (" + stringtr[i] + "(" + (Integer)thistr.get(i) + "," + tr_chosen_indices[i] + "))";
		  }
		  alignment += " )";
		*/
		
		ArrayList thisret = new ArrayList();
		thisret.add( "" );  //  thisret.add( alignment );  
		thisret.add( tr_chosen_indices );
		
		ret.add(thisret);
	    }
	}

	// return Alignment String / alignment array pairs
	return ret;
    }

    // returns an ArrayList of (ArrayList of Integers)s which are sorted.
    private static ArrayList getOrderings( ArrayList list, ArrayList indices, ArrayList common, ArrayList wordcount_this, ArrayList wordcount_other ) {

	ArrayList wordplacement = new ArrayList(); // the indices of each "common" word in the "list"
	for(int i=0; i < common.size(); i++) {
	    wordplacement.add(new ArrayList());
	}
	for(int i=0; i < list.size(); i++) {
	    String here = (String)list.get(i);
	    int index_in_common = common.indexOf(here);
	    // WATCH OUT FOR CLONE PROBLEMS ON NEXT LINE!! -->
	    Integer index_original = (Integer)indices.get(i);
	    ((ArrayList)wordplacement.get(index_in_common)).add(index_original);
	}

	// ********************************************************
	if(DEBUG) {
	    System.out.println("wordplacement:");
	    for(int i=0; i < wordplacement.size(); i++) {
		System.out.println("  "+ (ArrayList)wordplacement.get(i));
	    }
	} // OK
	// ********************************************************

	// Finally...build the wordpositions array
	ArrayList wordpositions = new ArrayList();
	for(int i=0; i < common.size(); i++) {
	    ArrayList thislist = (ArrayList)wordplacement.get(i);

	    // pick the smaller of the wordcounts
	    int select = ((Integer)wordcount_this.get(i)).intValue();
	    if( ((Integer)wordcount_other.get(i)).intValue() < select ) {
		select = ((Integer)wordcount_other.get(i)).intValue();
	    }
	    
	    if(DEBUG) System.out.println(" Select = " + select );
	    
	    ArrayList filled_array = new ArrayList();
	    fill( new ArrayList(), select, 0, thislist, filled_array);
	    wordpositions.add( filled_array );
	}
	
	// ********************************************************
	if(DEBUG) {
	    System.out.println("wordpositions:");
	    for(int i=0; i < wordpositions.size(); i++) {
		System.out.print("  ");
		ArrayList thislist = (ArrayList)wordpositions.get(i);
		for(int j=0; j < thislist.size(); j++) {
		    System.out.print((ArrayList)thislist.get(j) + " ");
		}
		System.out.println();
	    }
	}
	// ********************************************************
	
	ArrayList ret = new ArrayList();
	build( new ArrayList(), 0, wordpositions, ret );
	for(int i=0; i < ret.size(); i++) {
	    Collections.sort( (ArrayList)ret.get(i) );
	}
	
	return ret;
    }

    
    private static void fill( ArrayList soFar, int left_to_fill, int pos, ArrayList fromList, ArrayList finalResult ) {
	if( left_to_fill == 0 ) {
	    // success! add it to the finalResult
	    finalResult.add( (ArrayList)soFar.clone() );
	    return;
	}
	// check if it's even possible to use this branch
	else if( left_to_fill > (fromList.size()-pos) ) { return; }
	else {
	    ArrayList copy = (ArrayList)soFar.clone();
	    copy.add( (Integer)fromList.get(pos) );
	    fill( copy, left_to_fill-1, pos+1, fromList, finalResult );
	    fill( soFar, left_to_fill, pos+1, fromList, finalResult );
	}
    }


    // wordpositions matches up with the "common" words. It contains ArrayLists
    //    of Integers, which correspond to the positions of the words chosen
    //    from the ORIGINAL TR OR REF list.
    // THEREFORE, we return(build) an ArrayList (finalResult) of (ArrayLists of Integers), and we'll
    //    sort the ArrayLists of Integers. Then you retrieve the words from the
    //    original tr list in order from the ArrayList values. :)
    private static void build( ArrayList soFar, int pos, ArrayList wordpositions, ArrayList finalResult ) {
	//if( pos == wordpositions.size() ) {
	    ArrayList groups = (ArrayList)wordpositions.get(pos);
	    for(int i=0; i < groups.size(); i++) {
		ArrayList ints = (ArrayList)groups.get(i);
		ArrayList copy = (ArrayList)soFar.clone();
		for(int j=0; j < ints.size(); j++) {
		    // WATCH OUT FOR UNCLONED PROBLEMS HERE ->
		    copy.add((Integer)ints.get(j));
		}
		if( pos == wordpositions.size()-1 ) {
		    finalResult.add(copy);
		}
		else {
		    // CHECK THAT THIS IS IT!! :)
		    build( copy, pos+1, wordpositions, finalResult );
		}
	    }
	    //	    return;
	    //	}
	    //	else {
	    
	    //	}
    }

    private static String[] parseFile(String filename) {
	String[] ret = new String[2];
	try {
	    FileInputStream fis = new FileInputStream(filename);
	    
	    // NOTE: IF Using a charset other than standard ASCII, 
	    //   then enter it in the constructor on the following line
	    InputStreamReader isr = new InputStreamReader(fis);
	    
	    // Setup the input reader
	    BufferedReader in = new BufferedReader( isr );

	    // Read the file
	    ret[0] = in.readLine();
	    ret[1] = in.readLine();
	} catch(Exception e) {
	    System.out.println("Error in parseFile(): " + e);
	    System.exit(0);
	}
	return ret;
    }
    
    public static int getScore(int[] arr) {
	
	ArrayList sorted_arr = new ArrayList();
	for(int i=0; i < arr.length; i++) {
	    sorted_arr.add(new Integer(arr[i]));
	}
	Collections.sort(sorted_arr);
	
	// convert arr to values 0 - arr.length
	ArrayList smush = new ArrayList();
	for(int i=0; i < arr.length; i++) {
	    int num = arr[i];
	    for(int j=0; j < sorted_arr.size(); j++) {
		if( ((Integer)sorted_arr.get(j)).intValue() == num ) {
		    smush.add(new Integer(j));
		    break;
		}
	    }
	}
	
	for(int i=0; i < smush.size()-1; i++) {
	    int here = ((Integer)smush.get(i)).intValue();
	    int next = ((Integer)smush.get(i+1)).intValue();
	    if( here == next-1 ) {
		smush.remove(i);
		i--;
	    }
	}
	
	int[] curr = new int[smush.size()];
	for(int i=0; i < smush.size(); i++) {
	    curr[i] = ((Integer)smush.get(i)).intValue();
	}

	// NOTE! THIS IS WITHOUT CHUNKING
	//	int[] curr = (int[])arr.clone();
	
	int score = 0;
	boolean changed = false;
	do {
	    changed = false;
	    for(int j=0; j < curr.length - 1; j++) {
		if( curr[j] > curr[j+1] ) {
		    int temp = curr[j];
		    curr[j] = curr[j+1];
		    curr[j+1] = temp;
		    score++;
		    changed = true;
		}
	    }
	} while(changed);

	/**************************************
	if(DEBUG) {
	    for(int i=0; i < curr.length; i++) {
		System.out.print(curr[i] + " ");
	    }
	    System.out.println();
	}
	// **************************************/
	
	return score;
    }

}
