UVA1326 Jurassic Remains

UVA1326 Jurassic Remains

知识点:集合的整数表示 + 折半枚举 +异或

一.题意

选择最多的串,使得这些串中出现的每一位大写字母都是出现了偶数次,并且输出串的个数,和按照升序说出选择了哪几个串

二.题解

把二进制上的每一位当作是一个大写字母(A->1,B->2,C->4……),然后通过异或来判断出现的次数是奇次还是偶次,并且通过折半枚举,将n分成两半来枚举

#include<stdio.h>
#include<string.h>
#include<set>
#include<map>
#include<string>
#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;

int n,a[30];
map <int,int> table;

int bitcount(int x){	//求x对应二进制中1的个数 
	return (x == 0 ? 0 : (x & 1) + bitcount(x>>1));
}

int main(){
	while(cin>>n){
		string s;
		for(int i = 0; i < n; i++){
			cin>>s;
			a[i] = 0;
			for(int j = 0; j < s.size(); j++)
				//记录第i串中出现的每个字母的奇偶,奇数次为1,偶数次为0 
				a[i] ^= (1 <<  (s[j] - 'A'));	
		}
		
		//将n分成两半 
		int left = n/2;
		int right = n - left;
		
		//多组数据,每次清空table表 
		table.clear();
		
		for(int i = 0; i < (1<<left); i++){ //枚举含有全部n/2个元素的集合 
			int x = 0;
			for(int j = 0; j < left; j++)	if(i >> j & 1)//当i >> j为1时就代表选择了第j串 
				x ^= a[j];
			
			//x没出现过或者更新为一个同样得到结果为x,但是选择了更多串的结果	
			if(!table.count(x) || bitcount(table[x]) < bitcount(i))
				table[x] = i;
		}
		
		int res = 0;
		for(int i = 0; i < (1<<right); i++){
			int x = 0;
			for(int j = 0; j < right; j++)	if(i >> j & 1)
				x ^= a[left + j];
			//如果这个x出现过那么异或结果就为0,即出现过的每个大写字母都是偶数次
			//并且每次更新为选择串更多的结果	
			if(table.count(x) && bitcount(res) < (bitcount(table[x]) + bitcount(i)))
				//别忘记前面还有选择了的串,就把前面的串向左移动left位,在异或table[x] 
				res = (i<<left) ^ table[x];
		}
		
		printf("%d\n",bitcount(res));
		
		int k = 1;
		while(res){
			if(res & 1)
				printf("%d ",k);
			k++;
			res >>= 1;
		}
		puts("");
	}
	return 0;
}