#include <iostream>
#include <fstream>
#include <vector>
#include <set>
#include <map>
#include <string>

using namespace std;

/*
     Ovo je probni tekst. 		Pa druga recenica! Pa treca? Pa cetvrta    
     Jos jedna recenica.
*/

class Tekst
{
public:
	typedef set<unsigned> tPojavljivanja;
	typedef map<string,tPojavljivanja> tKatalogReci;

	Tekst( char* imedat )
		{
		ifstream f(imedat);
		if( !f )
			throw string("Nije uspelo otvaranje datoteke!");
		
		CitajRecenice( f );
		PripremiKatalogReci();
		}
		
	void Pronadji( string uslov, tPojavljivanja& rezultat ) const
		{
		set<string> obavezne, trazene, zabranjene;
		AnalizaUpita( uslov, obavezne, trazene, zabranjene );
		
		if( obavezne.size() > 0 ){
			set<string>::iterator
				i = obavezne.begin(),
				e = obavezne.end();
			tKatalogReci::const_iterator f = _Reci.find(*i);
			if( f != _Reci.end() )
				rezultat = f->second;
			for( i++; i!=e; i++ ){
				tKatalogReci::const_iterator f = _Reci.find(*i);
				if( f != _Reci.end() ){
					tPojavljivanja presek;
					tPojavljivanja::const_iterator
						pi = f->second.begin(),
						pe = f->second.end();
					for( ; pi!=pe; pi++ )
						if( rezultat.count(*pi)>0 )
							presek.insert(*pi);
					rezultat = presek;
					}
				}
			}
		else{
			set<string>::iterator
				i = trazene.begin(),
				e = trazene.end();
			for( ; i!=e; i++ ){
				tKatalogReci::const_iterator f = _Reci.find(*i);
				if( f != _Reci.end() ){
					tPojavljivanja::const_iterator
						pi = f->second.begin(),
						pe = f->second.end();
					for( ; pi!=pe; pi++ )
						rezultat.insert(*pi);
					}
				}
			}

		set<string>::iterator
			i = zabranjene.begin(),
			e = zabranjene.end();
		for( ; i!=e; i++ ){
			tKatalogReci::const_iterator f = _Reci.find(*i);
			if( f != _Reci.end() ){
				tPojavljivanja razlika;
				tPojavljivanja::iterator
					pi = rezultat.begin(),
					pe = rezultat.end();
				for( ; pi!=pe; pi++ )
					if( !f->second.count(*pi)>0 )
						razlika.insert(*pi);
				rezultat = razlika;
				}
			}
		}

	void IspisiRezultat( ostream& f, tPojavljivanja s ) const
		{
		tPojavljivanja::const_iterator
			si = s.begin(),
			se = s.end();
		for( ; si!=se; si++ )
			f << *si << " : " << _Recenice[*si] << endl;
		}
		
	void IspisiKatalogReci( ostream& f ) const
		{
		tKatalogReci::const_iterator 
			ri = _Reci.begin(),
			re = _Reci.end();
		for( ; ri!=re; ri++ ){
			f << ri->first << " - ";
			tPojavljivanja::const_iterator
				si = ri->second.begin(),
				se = ri->second.end();
			for( ; si!=se; si++ )
				f << *si << ' ';
			f << endl;
			}
		}
		
	void IspisiRecenice( ostream& f ) const
		{
		for( unsigned i=0; i<_Recenice.size(); i++ )
			f << i << " - " << _Recenice[i] << " (" << _Recenice[i].length() << ")" << (int)_Recenice[i][0] << endl;
		}
		
private:
	void CitajRecenice( istream& f )
		{
		string red;
		
		while( getline( f, red ) ){
			unsigned kraj = 0;
			while( kraj < red.size() - 1 ){
				// pronadjemo pocetak
				unsigned pocetak = red.find_first_not_of( " \t\r\n", kraj );
				if( pocetak == string::npos )
					break;
				// pronadjemo kraj
				kraj = red.find_first_of( ".?!", pocetak );
				if( kraj == string::npos )
					kraj = red.find_last_not_of( " \t\r\n" );
				kraj++;
				// izdvojimo recenicu i zapamtimo je
				_Recenice.push_back( red.substr( pocetak, kraj-pocetak ) );	
				}
			}
		}
		
	void PripremiKatalogReci()
		{
		string slova = "abcdefghijklmnopqrstuvwxyz";
		for( unsigned i=0; i<_Recenice.size(); i++ ){
			string recenica = _Recenice[i];
			for( unsigned i=0; i<recenica.size(); i++ )
				if( recenica[i]>='A' && recenica[i]<='Z' )
					recenica[i] += 'a' - 'A';
			unsigned kraj = 0;
			while( kraj < recenica.size() - 1 ){
				// pronadjemo pocetak
				unsigned pocetak = recenica.find_first_of( slova, kraj );
				if( pocetak == string::npos )
					break;
				// pronadjemo kraj
				kraj = recenica.find_first_not_of( slova, pocetak );
				if( kraj == string::npos )
					kraj = recenica.size();
				// izdvojimo rec i zapamtimo je
				_Reci[recenica.substr( pocetak, kraj-pocetak )].insert(i);
				}
			}
		}
		
	void AnalizaUpita( string uslov, set<string>& obavezne, set<string>& trazene, set<string>& zabranjene ) const
		{
		string slova = "abcdefghijklmnopqrstuvwxyz";
		for( unsigned i=0; i<uslov.size(); i++ )
			if( uslov[i]>='A' && uslov[i]<='Z' )
				uslov[i] += 'a' - 'A';
		unsigned kraj = 0;
		while( kraj < uslov.size() - 1 ){
			// pronadjemo pocetak
			unsigned pocetak = uslov.find_first_of( slova, kraj );
			if( pocetak == string::npos )
				break;
			// pronadjemo kraj
			kraj = uslov.find_first_not_of( slova, pocetak );
			if( kraj == string::npos )
				kraj = uslov.size();
			// izdvojimo rec i zapamtimo je
			string rec = uslov.substr( pocetak, kraj-pocetak );
			if( pocetak>0 && uslov[pocetak-1] == '+' )
				obavezne.insert( rec );
			else if( pocetak>0 && uslov[pocetak-1] == '-' )
				zabranjene.insert( rec );
			else
				trazene.insert( rec );
			}
		}
	
	vector<string>	_Recenice;
	tKatalogReci	_Reci;
};

main( int argc, char** argv )
{
	try	{
		if( argc < 2 )
			throw string( "Nedostaje naziv datoteke!" );

		Tekst t( argv[1] );
		//	t.IspisiRecenice( cout );
		//	t.IspisiKatalogReci( cout );
		string uslov;
		while( getline( cin, uslov ) ){
			Tekst::tPojavljivanja rezultat;
			t.Pronadji( uslov, rezultat );
			t.IspisiRezultat( cout, rezultat );
			}	
		}
	catch( const string& s ){
		cerr << "GRESKA: " << s << endl;
		}
	return 0;	
}

