思路:
用一个数的每一位表示在这个状态下字符的状态,1表示有奇数个,0表示有偶数个。
把所有字符串分成两半。
在前一半枚举所有可能取的情况,把异或值和长度存入map<int,STT> stt。
可以知道,如果前一半取了某些串使得奇数次的字符组成了一个集合ODD,那么后一半一定也取出ODD这种情况才能使异或值为0。
所以仍然在后一半枚举所有可能取的情况,假设当前计算出来的数为x,如果stt中存在x,就更新最长的长度。
注意:不能把代表每一个字符串取或不取的数和每个字符有或没有的数混淆。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 24
#define maxs 5000
struct STT{
int x,len;
STT(int xx=0,int ll=0){
x=xx,len=ll;
}
};
int n;
int a[maxn+5];
map<int,STT> stt;
void init() {
stt.clear();
}
void print(int ans,int h) {
printf("%d\n",ans);
if(ans) {
int j=0;
while(h){
if(h&1) printf("%d ",j+1);
h>>=1;
j++;
}
printf("\n");
}
printf("\n");
}
void readin() {
char s[1000];
for(int i=0; i<n; i++) {
scanf("%s",s);
int len=strlen(s);
int x=0;
for(int j=0; j<len; j++) {
int y=s[j]-'A';
x^=(1<<y);
}
a[i]=x;
}
}
void make_f() { //前半段
for(int i=0; i<(1<<(n/2)); i++) {
int x=i,y=0;
int s=0;
while(x) {
if(x&1) {
s^=a[y];
}
x>>=1;
y++;
}
int j=0;
y=i;
while(y){
j+=(y&1);
y>>=1;
}
if(!stt.count(s)||stt[s].len<j) {
stt[s]=STT(i,j);
}
}
}
void make_b() { //后半段
int ans=0,h;
for(int k=0; k<(1<<(n-n/2)); k++) { //注意不能写成 k<(1<<(n/2))
int i=k<<(n/2);
int x=i,y=0;
int s=0;
while(x) {
if(x&1) {
s^=a[y];
}
x>>=1;
y++;
}
int j=0;
y=i;
while(y){
j+=(y&1);
y>>=1;
}
if(stt.count(s)&&stt[s].len+j>ans) { //如果stt[i].len==0则说明在前半段中并未出现这样的值
ans=stt[s].len+j;
h=i+stt[s].x;
}
}
print(ans,h);
}
int main() {
while(~scanf("%d",&n)) {
init();
readin();
make_f();
make_b();
}
return 0;
}