sexta-feira, 18 de julho de 2008

Google Code Jam - Saving the Universe

from Google Code Jam - july/2008....
http://code.google.com/codejam

Problem

The urban legend goes that if you go to the Google homepage and search for "Google", the universe will implode. We have a secret to share... It is true! Please don't try it, or tell anyone. All right, maybe not. We are just kidding.

The same is not true for a universe far far away. In that universe, if you search on any search engine for that search engine's name, the universe does implode!

To combat this, people came up with an interesting solution. All queries are pooled together. They are passed to a central system that decides which query goes to which search engine. The central system sends a series of queries to one search engine, and can switch to another at any time. Queries must be processed in the order they're received. The central system must never send a query to a search engine whose name matches the query. In order to reduce costs, the number of switches should be minimized.

Your task is to tell us how many times the central system will have to switch between search engines, assuming that we program it optimally.

Input

The first line of the input file contains the number of cases, N. N test cases follow.

Each case starts with the number S -- the number of search engines. The next S lines each contain the name of a search engine. Each search engine name is no more than one hundred characters long and contains only uppercase letters, lowercase letters, spaces, and numbers. There will not be two search engines with the same name.

The following line contains a number Q -- the number of incoming queries. The next Q lines will each contain a query. Each query will be the name of a search engine in the case.

Output

For each input case, you should output:

Case #X: Y
where X is the number of the test case and Y is the number of search engine switches. Do not count the initial choice of a search engine as a switch.

Limits

0 < N ≤ 20

Small dataset

2 ≤ S ≤ 10

0 ≤ Q ≤ 100

Large dataset

2 ≤ S ≤ 100

0 ≤ Q ≤ 1000

Sample


Input

Output
2
5
Yeehaw
NSM
Dont Ask
B9
Googol
10
Yeehaw
Yeehaw
Googol
B9
Googol
NSM
B9
NSM
Dont Ask
Googol
5
Yeehaw
NSM
Dont Ask
B9
Googol
7
Googol
Dont Ask
NSM
NSM
Yeehaw
Yeehaw
Googol

Case #1: 1
Case #2: 0

In the first case, one possible solution is to start by using Dont Ask, and switch to NSM after query number 8.
For the second case, you can use B9, and not need to make any switches.


SOLUTION

/**
*
*/
package codejam;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;

/**
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
public class SavingUniverse {

private static final String FILE_NAME = "/tmp/sample.txt";


/**
* pass the full location of the sample file as argument
* if you dont pass anything it will search at /tmp/sample.txt
*
* @param args
*/
public static void main(String[] args) {
String fname = args.length == 0 ? FILE_NAME : args[0];
try {
SavingUniverse o = new SavingUniverse();
o.start(fname);
} catch (Exception e) {
System.out.println("Oppps.");
System.out.println("An exception has been throw:" + e.getMessage());
e.printStackTrace();
}
}

private int n = 0; // # of cases
ArrayList listEngines = null; // search engines nodes
ArrayList listQueries = null;

private void start(String fileName) throws Exception {
File f = new File(fileName); // sample file
int s = 0; // # of search engines
int q = 0; // # of queries

if (!f.exists() || !f.isFile()) throw new Exception("Sorry, " + fileName + " is not a valid file.");
FileInputStream fi = new FileInputStream(f);
int cint;
String tmp = new String();
int caseNum = 0;
while ((cint = fi.read()) > -1) {
char c = (char) cint;
if (c == '\r' || c == '\n') {
if (tmp.length() > 0) {
if (n == 0) {
try {
n = Integer.parseInt(tmp);
} catch (NumberFormatException nfe) {
// do nothing. the 1st line should not be in correct format
}
} else if (q >0) {
addQuery(tmp);
q--;
if (q == 0) {
process(caseNum);
s = 0;
}
} else if (s == 0) {
s = Integer.parseInt(tmp);
if (listEngines == null){
listEngines = new ArrayList();
listQueries = new ArrayList();
} else {
listEngines.clear();
listQueries.clear();
}
caseNum++;
} else if (s > 0) {
listEngines.add(new SEngineNode(tmp));
s--;
if (s == 0) s = -1;
} else if (s == -1) {
q = Integer.parseInt(tmp);
if (q == 0) { // no queries huh?
print(caseNum, 0);
s = 0;
}
}
tmp = "";
}
} else if (c != ' ') {
tmp += c;
}
}
if (n != 0 && q > 0) {
addQuery(tmp.toString());
q--;
if (q == 0) {
process(caseNum);
}
}
}

/**
*
*/
private void process(int caso) {
// check if the 1st node has value of 0
int counter = 0;
for (int i = 0; i <>
QueryNode qrynode = listQueries.get(i);
SEngineNode engine = qrynode.node;
ArrayList farNodeList = cloneEngines(engine);
int j = i + 1;
for (j = i; j <>
QueryNode qrynodenxt = listQueries.get(j);
removeEngine(farNodeList, qrynodenxt);
if (farNodeList.size() == 0) break;
}
if (farNodeList.size() == 0) {
counter++;
i = j - 1;
} else {
break;
}
}
print(caso, counter);
}

/**
* @param farNodeList
* @param qrynodenxt
*/
private void removeEngine(ArrayList farNodeList, QueryNode qrynodenxt) {
String s = qrynodenxt.query;
for (SEngineNode n : farNodeList) {
if (s.indexOf(n.engineName) > -1) {
farNodeList.remove(n);
break;
}
}
}

/**
* @return
*/
private ArrayList cloneEngines(SEngineNode exceptNode) {
ArrayList ret = new ArrayList();
for (SEngineNode node : listEngines) {
if (!node.engineName.equals(exceptNode.engineName)) ret.add(node);
}
return ret;
}

/**
* @param caso
* @param i
*/
private void print(int caso, int i) {
System.out.println("Case #" + caso + ": " + i);
}

/**
* @param string
*/
private void addQuery(String s) {
s = s.toUpperCase();
for (SEngineNode node : listEngines) {
if (s.indexOf(node.engineName) > -1) {
node.qtd++;
listQueries.add(new QueryNode(s, node));
break;
}
}
}

class QueryNode {
public QueryNode(String _query, SEngineNode _engine) {
query = _query;
node = _engine;
}
String query;
SEngineNode node;
}

class SEngineNode {
public SEngineNode(String _engineName) {
engineName = _engineName.toUpperCase().trim();
}
int qtd; // qtd that match this search engine
String engineName;
}

}


quarta-feira, 16 de julho de 2008

Ambiguous Codes

An extensive area of research in computer science is the field of communications. With computer networks being part of everyday life of many people, the development of ways for making networks faster, more reliable and secure is constantly needed. This practical need motivates an extensive research activity in the theory behind communications.

The very first thing needed to establish any kind of communication is a common code. A code is a way of changing the form of a piece of information into some other form, in general to make it possible to convey that piece of information from one place to another. Flag codes used by boats and the Morse code used in telegraphy are examples of codes for translating letters into different forms to enable communication over different media.

More formally, a code is a set of strings composed of symbols from one alphabet. Each string defined in the code is called a code word. A message is then composed concatenating a set of code words to convey the information needed. For example, in Morse code the alphabet is composed of the symbols hyphen and dot; letter ``S" is represented by the code word ``...", letter ``O" is represented by the code word ``--", and therefore the distress message ``SOS" in Morse code is ``...--...".

Codes for communication can have many desirable and undesirable properties such as ambiguity, entropy, redundancy, and many more. In this problem we will focus on ambiguity as a key property.

A code is ambiguous when there exists a message using that code that can be partitioned into different sequences of code words. In other words, in an ambiguous code a message may have more than one meaning. For example, consider the binary alphabet, composed of symbols {0,1}. For the code composed of the words {10, 01, 101} the message 10101 can be understood as 10-101 or 101-01 and therefore the code is ambiguous. On the other hand, for the code composed of the words {01, 10, 011} no ambiguous message exists and therefore the code is unambiguous.

As a part of the computer science community, you are required to develop a tester that checks if codes are ambiguous. In case a code is indeed ambiguous, you are also required to report the length (i.e. the number of symbols) of the shortest ambiguous message for that code.

Input

Each test case will consist on several lines. In all test cases the alphabet will be the set of hexadecimal digits (decimal digits plus the uppercase letters ``A" to ``F"). The first line of a test case will contain an integer N (1$ \le$N$ \le$100) , the number of code words in the code. Each of the next N lines describes a code word and contains a different and non-empty string of at most 50 hexadecimal digits.

Input is terminated by N = 0 .

Output

For each test case, output a single line with the length of the shortest ambiguous message for the provided code or `-1' if the code is unambiguous.

Sample Input

3
10
01
101
3
AB
BA
ABB
0

Sample Output

5

Solution


/**
* Exemplo para resolver o problema de AmbiguousCode
* Este programa usa uma árvore ordenada priorizada pelos elementos de
* menor tamanho. Usa um hash para guardar os valores encontrados e se encontrar
* outro igual gera uma exception onde a mensagem é a String ambigua.
*
* Não implementei para ler de um arquivo texto, como no enunciado. Isto é muito fácil ;)
*
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*/
package codejam;

import java.util.concurrent.PriorityBlockingQueue;

import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;

/**
* @author Ricardo A Harari - ricardo.harari@gmail.com
*
*/
public class AmbiguousCode {

private final String[] array = new String[] { "01", "AB", "BA", "ABB", "10", "101" };
//private final String[] array = new String[] { "AB", "BA", "ABB" };

private final Hashtable ht = new Hashtable();

/**
*
* @param pq
* @throws Exception
*/
private void process(PriorityBlockingQueue pq) throws Exception {
if (pq.size() == 0) throw new Exception("-1");
Node father = pq.poll();
for (int j = 0; j <>
Node child = new Node();
child.father = father;
child.step = child.father.step + 1;
child.valueFull = child.father.valueFull;

String s = array[j];
Node check = child.father;
boolean found = false;
while (!found && check != null) {
found = check.value.equals(s);
check = check.father;
}
if (!found) {
child.value = s;
child.valueFull += s;
if (ht.get(child.valueFull) != null) throw new Exception(child.valueFull);
ht.put(child.valueFull, child.valueFull);
pq.add(child);
}
}
}

/**
*
* @param args
*/
public static void main(String[] args) {
AmbiguousCode c = new AmbiguousCode();
c.start();
}

/**
* inicio do processo
*/
private void start() {
try {
PriorityBlockingQueue pq = new PriorityBlockingQueue(array.length * array.length);
pq.clear();
// adiciona os primeiros elementos na priorityqueue
for (int i = 0; i <>
Node n = new Node();
n.value = array[i];
n.valueFull = n.value;
if (ht.get(array[i]) != null) throw new Exception(array[i]);
ht.put(n.value, n.value);
pq.offer(n);
}
// enquanto tiver elementos, processar
while (pq.size() > 0) {
process(pq);
}
System.out.println("-1");
} catch (Exception e) {
System.out.println(e.getMessage().length());
}
}

/**
* nó
*
* @author Ricardo Alberto Harari - ricardo.harari@gmail.com
*
*/
class Node implements Comparable {
Node father = null;
String value = null;
String valueFull = null;
int step = 0;

/*
* o segredo esta em comparar o tamanho da string
*
* desta forma as menores serão processadas primeiro.
*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Node arg0) {
int l1 = valueFull.length();
int l2 = arg0.valueFull.length();
return l1 - l2;
}
}

}