F. Dasha and Nightmares(二进制)
题目最重要的破题点思路 即第4点 由某银牌佬提供,唉膜拜大佬。
题意
给定
n
n
n 个字符串,
(
1
<
=
n
<
=
2
∗
1
0
5
)
(1 <=n<=2 * 10^5)
(1<=n<=2∗105) 字符串长度之和不超过
5
∗
1
0
6
5 *10^6
5∗106。
求字符串对
s
i
,
s
j
si,sj
si,sj个数,要满足以下条件
条件1.拼接后恰好有
25
25
25 种字母。
条件2.拼接后每个字母个数为奇数。
题解 + 思考过程
1. 最开始想到用二进制,将每个字符串转化为一个长度为
26
26
26 的二进制数,字符
c
h
ch
ch 的个数, 奇数
=
1
=1
=1 ,偶数
=
0
=0
=0
例如
s
t
r
=
"
a
"
str = "a"
str="a" 二进制串
=
1000
…
…
= 1000……
=1000……,
s
t
r
=
"
a
b
"
=
1100
…
…
str = "ab" = 1100……
str="ab"=1100……,
s
t
r
=
"
a
a
b
"
=
0100
…
…
str = "aab" = 0100……
str="aab"=0100……
对于条件1:可以想到对于每个字符串枚举哪一个字符舍去。
对于条件2:用位运算直接去找到合法的,异或全
1
1
1 二进制数再减去应该舍去的字符位上的1。
2. 但是必须只有
25
25
25 个,但是偶数对应
0
0
0, 那么不知道该字符是否有,因为没有该字符为
0
0
0 也对应
0
0
0。
例如枚举字符
a
a
a 没有, 但字符串
s
t
r
=
"
a
a
"
str = "aa"
str="aa" ,那么该字符串代表的二进制为全
0
0
0,就可以和 字符串
"
b
∼
z
"
"b\sim z"
"b∼z" 拼接 但拼接后有
26
26
26个字母,显然不合法。
3. 于是想到用三进制来表示,
0
=
0
0 = 0
0=0, 奇数
=
1
= 1
=1 大于
0
0
0 的偶数
=
2
= 2
=2, 用字典树去计算
但是由于
1
1
1 对应
0
0
0 和
2
2
2 (字符串该位上为
1
1
1,那么该位上为
0
0
0 或
2
2
2 的都可以与之拼接)
这样递归时间复杂度可能达到
2
2
2 的幂次方级别。不可行。
4. 最后想到 枚举的不存在的那个字符,只有两个字符串都没有该字符才能拼接后满足。
所以我们可以每次遍历不存在 字符
c
h
ch
ch 的字符串集合,这样就不用考虑该位的
0
0
0 是大于
0
0
0 的偶数还是
0
0
0 的问题了。
因为除了我们枚举的该位必须不存在的其他字符 都必须为奇数,所以至少存在,对应拼接的是
0
0
0 还是大于
0
0
0 的偶数就无所谓了。
可以直接采取1.做法,不过需要在每次计算后清除贡献。(如果不清除,影响可以参考2.)。
代码
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const int N = 2e5 + 10, M = 1 << 26;
const int K = (1 << 26) - 1;//长度为26的全1二进制数
string s[N];
int t[N], val[M];//t[i]:转化后二进制数 val[i]:计数
vector<int>g[26];//g[i]:没有字符i的字符串的下标
int main()
{
ios::sync_with_stdio(false);
cout.tie(NULL);
int n;
cin >> n;
for(int i = 1; i <= n; i ++){
cin >> s[i];
int len = s[i].length();
int vis[30] = {0};
for(int j = 0; j < len; j ++){
vis[s[i][j] - 'a'] ++;
}
int num = 0;
for(int j = 0; j < 26; j ++){
if(vis[j] & 1) num += (1 << j);
if(!vis[j]) g[j].push_back(i);
}
t[i] = num;
}
ll ans = 0;
for(int i = 0; i < 26; i ++){//枚举没有i字符的字符串,只有都没有某个字符的字符串才能匹配
for(int id : g[i]){
val[t[id]] ++;
int v = t[id] ^ K ^ (1 << i);//能与si拼接的字符串所代表的二进制数
ans += val[v];
}
for(int id : g[i]) val[t[id]] --;//清除贡献
}
cout << ans;
return 0;
}