PKU 3098-Frugal Search

問題概要

文字列集合に対して、マッチする辞書順最小の文字列を求める問題。ただしマッチする時に、|で区切られたどれかにマッチすれば良い。
マッチは、+が前に付いた文字を必ず含み、-が前に付いた文字は必ず含まず、さらにそれ以外の一文字以上を含むという条件で行なわれる。

解法

文字列集合を最初にソートし、問題文通りにマッチを実装する

実装(C++)

#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
vector<string> inp;
bool match2(const string &s,const string &q){
	vector<char> unsign,positive,negative;
	for(int i=0;i<(int)q.size();i++){
		if(q[i]=='+'){
			positive.push_back(q[i+1]);
			i++;
		}else if(q[i]=='-'){
			negative.push_back(q[i+1]);
			i++;
		}else{
			unsign.push_back(q[i]);
		}
	}
	bool containUnsigned=false;
	sort(unsign.begin(),unsign.end());
	//最低1つunsignを含むかどうか
	for(int i=0;i<(int)s.size();i++){
		if(binary_search(unsign.begin(),unsign.end(),s[i])){
			containUnsigned=true;
			break;
		}
	}
	if(containUnsigned==false)return false;

	//全てのpositiveを含み、かつnegativeを1つも含まないか
	sort(positive.begin(),positive.end());
	positive.erase(unique(positive.begin(),positive.end()),positive.end());
	sort(negative.begin(),negative.end());
	for(int i=0;i<(int)s.size();i++){
		if(binary_search(negative.begin(),negative.end(),s[i]))return false;
		if(binary_search(positive.begin(),positive.end(),s[i])){
			positive.erase(lower_bound(positive.begin(),positive.end(),s[i]));
		}
	}
	return positive.empty(); //全てのpositiveを使ったか
}
bool match(const string &s,const string &q){
	if(s=="NONE")return true; //番兵
	string _q="";
	for(int i=0;i<(int)q.size();i++){
		if(q[i]=='|'){
			if(match2(s,_q))return true;
			_q="";
		}else{
			_q.push_back(q[i]);
		}
	}
	return match2(s,_q);
}
int main() {
	while(true){
		string s;
		inp.clear();
		while(true){
			cin>>s;
			if(s=="#")return 0;
			if(s=="*")break;
			inp.push_back(s);
		}
		sort(inp.begin(),inp.end()); //辞書順にする
		inp.push_back("NONE");
		while(true){
			cin>>s;
			if(s=="**")break;
			for(int i=0;i<(int)inp.size();i++){
				if(match(inp[i],s)){
					cout<<inp[i]<<endl;
					break;
				}
			}
		}
		cout<<"$"<<endl;
	}
	return 0;
}