【题目来源】
https://vjudge.net/problem/UVALive-2965
【题意】找最多字符串使得所有字符出现次数为偶数次。
在一个字符串中,每个字符出现的次数本身是无关紧要的,重要的只是这些次数的奇偶性,因此想到用一个二进制的位表示一个字母(11表示出现奇数次,00表示出现偶数次)。比如样例的66个数,写成二进制后如图所示。
此时,问题转化为求尽量多的数,使得它们的xorxor值为00。
最容易想到的方法是直接穷举,时间复杂度为O(2n)O(2n),有些偏大。注意到xorxor值为00的两个整数必须完全相等,我们可以把字符串分成两个部分:首先计算前n2n2个字符串所能得到的所有xorxor值,并将其保存到一个映射SS(xorxor值->前n2n2个字符串的一个子集)中;然后枚举后n2n2个字符串所能得到的所有xorxor值,并每次都在SS中查找。
如果映射用STLSTL的mapmap实现,总时间复杂度为O(2n2log2n)O(2n2log2n),即O(1.44nlog2n)O(1.44nlog2n),比第一种方法好了很多。
【代码】
下面是自己打的,我感觉时间就是节约在消掉一些重复的,然后在互相枚举。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[30][N];
map<int,int>ma;
int a[N];
int solo(int x)
{
if(x==0) return 0;
return solo(x/2)+x%2;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;++i)
{
scanf("%s",s[i]+1);
int l=strlen(s[i]+1);
a[i]=0;
for(int j=1;j<=l;++j)
{
int d=s[i][j]-'a';
a[i]^=(1<<d);//预处理每一个字符串字符出现的情况,
//用26位二进制表示,0号位有1代表有奇数个‘a’
}
}
ma.clear();
int n1=n/2,n2=n-n1;
for(int i=0;i<(1<<n1);++i)
{
int x=0;
for(int j=0;j<n1;++j)
{
if((1<<j)&i) x^=a[j+1];
}
int tmp=solo(i);
if(ma[x]==0||solo(ma[x])<tmp) ma[x]=i;//ma[x],x是一个字符串内的字符情况
//ma[x]是总体
}
int ans=0;
for(int i=0;i<(1<<n2);++i)
{
int x=0;
//a[n1+i]
for(int j=0;j<n2;++j)
{
if((1<<j)&i) x^=a[n1+j+1];
}
//printf("n2:%d x:%d\n",n2,x);
int tmp=solo(i);
if(ma[x]&&solo(ma[x])+solo(i)>solo(ans))
{
ans=(i<<n1)^ma[x];
}
}
int x=ans;
printf("%d\n",solo(ans));
int id=1;
while(x)
{
if(x&1) printf("%d ",id);
++id;
x>>=1;
}
puts("");
}
return 0;
}