import java.io.*;
import java.util.Vector;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Vector;
import java.util.Set;
import java.util.HashSet;
import java.util.List;
import java.util.Iterator;

public class I328
{
	public static void main(String[] args) throws IOException
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("be.txt")));
		List<Expression> expressions = new Vector<Expression>();
		while (br.ready()) expressions.add(new Expression(br.readLine())); //beolvassuk a sorokat, és Expression pédányokat készítünk belőle
		br.close();
		
		int contradictionLineNumber = getContradictionLineNumber(expressions);
		//System.out.println("ellentmondas: " + contradictionLineNumber);
		PrintWriter pw = new PrintWriter(new FileOutputStream("ki.txt"));
		pw.println(contradictionLineNumber);
		pw.close();
	}
	
	/** Kiszámítja az első sort, amely ellentmondásban van az előzőekkel */
	private static int getContradictionLineNumber(List<Expression> expressions)
	{
		List<String> variables = new Vector<String>(); //ez a lsita az abc első 10 betűjéből álló változókat tartalmazza
		for (Expression ex : expressions) variables.addAll(Arrays.asList(Expression.splitStringByOperatorsAndBrackets(ex.getString()))); //feltöltjük a listát
		removeRepeatingElements(variables); //egy betűt csak egyszer tárolunk el
		removeEmptyStrings(variables); //az üres String-eket amik a daraboláskor keletkeztek eltávolítjuk
		
		int max = 0; //a legnagyobb sorszámú sor, amelynél ellentmodásba ütköztünk (az első sor indexe: 0)
		for (int n = 0; n < 1 << (variables.size() + 1); n++) //az összes váltózó értékeit az összes lehetséges kominációban előállítjuk (az n szám bitjei határozzák meg, hogy az egyes változók true vagy false értéket vesznek fel)
		{
			Map<String, Boolean> values = new HashMap<String, Boolean>(); //ez a Map tartalmazza a változók aktuális értékét
			values.put("", true); //a 'nem' művelet bal oldali argumentumánál fordul elő (lásd 298. sor) (értéke lehetne false is, nem lényges)
			values.put("false", false); //számítás közben egyes részeket "true", "false" Stringekre cserélünk ki
			values.put("true", true);
			for (int i = 0; i < variables.size(); i++)
			{
				values.put(variables.get(i), getBitAtIndex(n, i)); //beállítja a vátozók értékel n-től függően
			}
			
			int i;
			for (i = 0; i < expressions.size() && expressions.get(i).evaluate(values); i++); //megkeresi az első hamis állíás sorát az aktuális változó érétkekre (az i-t addig növeljük, amíg az i-edik sor nem ad hamis érétket)
			
			max = Math.max(max, i);
			if (max == expressions.size()) //ha az összes sor igaz, akkor nincs ellentmondás, visszatérunk 0-val
			{
				return 0;
			}
		}
		
		return max + 1; //a 0-tól kezdődő indexelés miatt növeljük 1-gyel
	}
	
	/** 2-es számrendszerben egy szám megadott sorszámú helyiérétkén levő bitet adja meg */
	private static boolean getBitAtIndex(int number, int index)
	{
		return ((number >> index) & 1) == 1;
	}
	
	private static void removeRepeatingElements(Iterable iterable)
	{
		Set elements = new HashSet();
		for (Iterator it = iterable.iterator(); it.hasNext();)
		{
			if(!elements.add(it.next())) it.remove();
		}
	}
	
	private static void removeEmptyStrings(Iterable<String> iterable)
	{
		for (Iterator<String> it = iterable.iterator(); it.hasNext();)
		{
			String s = it.next();
			if (s == null || s.equals("")) it.remove();
		}
	}
	
	/**
	 * Feldarabolja a Stringet a karakter mentén.
	 * Azért nem a String.split(String) metódust használjuk, mert az különleges írásjelekkel "nem működik"
	*/
	public static String[] split(String src, char chr)
	{
		Vector<String> strings = new Vector<String>();
		String current = "";
		
		for (char c : src.toCharArray())
		{
			if (c == chr)
			{
				strings.add(current);
				current = "";
			}
			else
			{
				current += c;
			}
		}
		
		strings.add(current);
		return strings.toArray(new String[strings.size()]);
	}
}

/**
 * 
*/
class Expression
{
	private static final char openBracket = '(';
	private static final char closeBracket = ')';
	
	private final String str;
	
	public Expression(String s)
	{
		this.str = s;
	}
	
	public String getString()
	{
		return this.str;
	}
	
	/**
	 * @param values a Map ami tárolja a változók érétkét
	 * @return a kifejezés érétke az adott változó értékek esetén.
	 * @exception IllegalArgumentException ha a Map nem tartalmazza egy változónak az érétkét
	 * @exception NullPointerException ha a Map null
	*/
	public boolean evaluate(Map<String, Boolean> values)
	{
		if (values == null) throw new NullPointerException();
		
		for (int i = 0; i < this.str.length(); i++) //megkeressük az első nyitó zárójelet
		{
			if (this.str.charAt(i) == openBracket)
			{
				int closeBracketIndex = this.findBracketPair(i); //megkeressük a párját
				boolean bracketValue = new Expression(this.str.substring(i + 1, closeBracketIndex)).evaluate(values); //a zárójel érétkét kiszámítjuk
				return new Expression(replaceRangeInString(this.str, i, closeBracketIndex + 1, String.valueOf(bracketValue))).evaluate(values); //kicseréljük a zárójelet "true" vagy "false" Stringre, és az így kapott kifejezés értékét adjuk vissza
			}
		}
		
		//erre a részre akkor jut el a program, ha nincs zárójel
		for (Operator op : Operator.reversedPrecedence)
		{
			String[] strArr = op.splitStringByOperatorAtLastIndex(this.str); //a legalacsonyabb precedenciájú művelet utolsó előfordulásánál (mivel ezt végezzük el utoljárja) kettébonjuk a kifejezést
			if (strArr.length == 1) continue;
			boolean value1 = new Expression(strArr[0]).evaluate(values); //a két rész érétkét kiszámítjuk, 
			boolean value2 = new Expression(strArr[1]).evaluate(values);
			return op.evaluate(value1, value2); //végül elvégezzük az utolsó műveletet
		}
		
		//erre a részre akkor jut el a program, ha nincs semmiylen művelet sem
		if (!values.containsKey(this.str)) throw new IllegalArgumentException("map hasn't mapping for key: " + this.str);
		return values.get(this.str); //ha nincs semmiylen művelet sem, akkor csak egy változó alkotja a kifejezést, amelynek érékét visszaadjuk
	}
	
	/** Feldarabolja az adott Stringet műveleti jelek és zárójelek mentén, ezáltal a változókat adja meg */
	public static String[] splitStringByOperatorsAndBrackets(String src)
	{
		Vector<String> strings = new Vector<String>();
		strings.add(src);
		
		Vector<Character> splitByChars = new Vector<Character>();
		for (Operator op : Operator.values()) splitByChars.add(op.getChar());
		splitByChars.add(openBracket);
		splitByChars.add(closeBracket);
		
		for (char splitByChar : splitByChars)
		{
			Vector<String> previousStrings = (Vector<String>)strings.clone();
			strings.clear();
			for (String current : previousStrings)
			{
				strings.addAll(Arrays.asList(I328.split(current, splitByChar)));
			}
		}
		
		return strings.toArray(new String[strings.size()]);
	}
	
	/** Kicseréli egy Sringnek a megadott tartományát egy másik Stringre */
	private static String replaceRangeInString(String src, int startIndex, int endIndex, String str)
	{
		String result = "";
		result += src.substring(0, startIndex);
		result += str;
		result += src.substring(endIndex, src.length());
		return result;
	}
	
	private int findBracketPair(int index)
	{
		int inc = getBracketCode(this.str.charAt(index));
		if (inc == 0) throw new IllegalArgumentException("In: " + this.str + " at: " + index + " is not a bracket");
		int bracketDiff = 0;
		for (int i = index; i >= 0 && i < this.str.length(); i += inc)
		{
			bracketDiff += getBracketCode(this.str.charAt(i));
			if (bracketDiff == 0) return i;
		}
		throw new IllegalArgumentException("cannot find pair of bracket in: " + this.str + " at: " + index);
	}
	
	private static int getBracketCode(char c)
	{
		switch (c)
		{
			case openBracket: return 1;
			case closeBracket: return -1;
			default: return 0;
		}
	}
	
	public String toString()
	{
		return this.getClass().getSimpleName() + "[str=" + this.str + "]";
	}
}

/** Egy műveleti jelet tárol reprezentál */
enum Operator
{
	AND('&'),
	OR('|'),
	NOT('.'),
	IMPLICATION('>');
	
	public static final Operator[] reversedPrecedence = new Operator[]{IMPLICATION, OR, AND, NOT};
	
	private final char chr;
	
	private Operator(char chr)
	{
		this.chr = chr;
	}
	
	public static Operator getOperatorByChar(char chr)
	{
		for (Operator op : values())
		{
			if (op.getChar() == chr) return op;
		}
		throw new IllegalArgumentException("No such Operator with char: " + chr);
	}
	
	public String[] splitStringByOperatorAtLastIndex(String src)
	{
		int lastIndex = src.lastIndexOf(this.getCharAsString());
		if (lastIndex < 0) return new String[] {src};
		return new String[] {src.substring(0, lastIndex), src.substring(lastIndex + 1, src.length())};
	}
	
	public char getChar()
	{
		return this.chr;
	}
	
	public String getCharAsString()
	{
		return new String(new char[]{this.getChar()});
	}
	
	/** Elvégzi a műveletet a két éréken */
	public boolean evaluate(boolean value1, boolean value2)
	{
		switch (this)
		{
			case AND: return value1 && value2;
			case OR: return value1 || value2;
			case NOT: return !value2; //a bal oldali argumentuma üres (hiszen a '.' bal oldalán nem áll változó), ezért csak a második argumentumot vesszük figyelembe (value1)
			case IMPLICATION: return !value1 || value2;
			default: throw new UnknownOperatorException(this);
		}
	}
	
	public String toString()
	{
		return this.getClass().getSimpleName() + "[char=" + this.getCharAsString() + "]";
	}
}

class UnknownOperatorException extends RuntimeException
{
	public UnknownOperatorException(Operator op)
	{
		super("Unknown operator: " + op);
	}
}
