题目传送门
PenguinQQ 是中国最大、最具影响力的 SNS(Social Networking Services)网站,以实名制为基础,为用户提供日志、群、即时通讯、相册、集市等丰富强大的互联网功能体验,满足用户对社交、资讯、娱乐、交易等多方面的需求。
小 Q 是 PenguinQQ 网站的管理员,他最近在进行一项有趣的研究——哪些账户是同一个人注册的。经过长时间的分析,小Q发现同一个人注册的账户名称总是很相似的,例如 Penguin1,Penguin2,Penguin3……于是小 Q 决定先对这种相似的情形进行统计。
小 Q 定义,若两个账户名称是相似的,当且仅当这两个字符串等长且恰好只有一位不同。例如“Penguin1”和“Penguin2”是相似的,但“Penguin1”和“2Penguin”不是相似的。而小 Q 想知道,在给定的 n n n 个账户名称中,有多少对是相似的。
为了简化你的工作,小Q给你的N 个字符串长度均等于L ,且只包含大小写字母、数字、下划线以及‘@’共64种字符,而且不存在两个相同的账户名称。
第一行包含三个正整数 N , L , S N,L,S N,L,S。其中 N N N 表示账户名称数量, L L L 表示账户名称长度, S S S 用来表示字符集规模大小,它的值只可能为 2 2 2 或 64 64 64。
若 S S S 等于 2 2 2,账户名称中只包含字符 0
和 1
共 2 2 2 种字符;
若 S S S 等于 64 64 64,账户名称中可能包含大小写字母、数字、下划线以及 @
共 64 64 64 种字符。
随后 N N N 行,每行一个长度为 L L L 的字符串,用来描述一个账户名称。数据保证 N N N 个字符串是两两不同的。
仅一行一个正整数,表示共有多少对相似的账户名称。
4 3 64
Fax
fax
max
mac
4
4 4 4 对相似的字符串分别为:Fax 与 fax,Fax 与 max,fax 与 max,max 与 mac。
测试点编号 | N N N | L L L | S S S |
---|---|---|---|
1 1 1 | 50 50 50 | 10 10 10 | 64 64 64 |
2 2 2 | 500 500 500 | 100 100 100 | 64 64 64 |
3 3 3 | 3000 3000 3000 | 100 100 100 | 2 2 2 |
4 4 4 | 3000 3000 3000 | 100 100 100 | 64 64 64 |
5 5 5 | 30000 30000 30000 | 50 50 50 | 2 2 2 |
6 6 6 | 30000 30000 30000 | 50 50 50 | 64 64 64 |
7 7 7 | 30000 30000 30000 | 200 200 200 | 2 2 2 |
8 8 8 | 30000 30000 30000 | 200 200 200 | 64 64 64 |
9 9 9 | 30000 30000 30000 | 200 200 200 | 2 2 2 |
10 10 10 | 30000 30000 30000 | 200 200 200 | 64 64 64 |
首先看到字符串,找串之间的相似关系,很自然的想到哈希
按照我这种菜狗的常规思想,第一个想到的就是暴力,两两比对,然后统计答案
但是这样的复杂度是 N 2 L 2 N^2L^2 N2L2 的,不能接受,考虑怎么优化
首先对于 L 2 L^2 L2 的部分,可以通过哈希来优化掉一个 L L L ,这样只需要遍历每一位,把中间的哈希值去掉,拼起来就可以了
看数据范围,发现复杂度如果是 N L NL NL 是完全可以接受的,还可以顺便带个维护某个数据结构的复杂度
继续考虑简化,考虑现在已经把某一位的字符全去掉了,如何统计有多少对相同的字符。
这个就很简单了,先这样再那样就可以了 , 可以丢到数组里面排序,或者放 m a p map map 里面也可以
那这样就可以 O ( N L ) O(NL) O(NL) 求出去掉每一位字符后的字符串哈希值,并且丢掉相应的数组里面
字符串哈希什么的最烦人了
因为进行字符串哈希,进行拼接的时候容易脑抽出错 ,哈希是一种思想没有固定的模版,可以将每一位的哈希值进行异或,运用一个数异或两次等于0这个性质,就可以很方便的去掉第 i i i 位了
对于每一位的哈希值也有很多种方法,随便套一种稍微复杂点的函数就行
#include
#define int unsigned long long
using namespace std;
inline unsigned long long randllu() { return static_cast<unsigned long long> (rand()); }
inline unsigned long long RAND() {
return randllu() << 48 ^ randllu() << 32 ^ randllu() << 16 ^ randllu();
}
int l, n, m;
int bit[300],ch[300];
vector<int> v[300];
signed main(){
srand(114514);
cin >> n >> l >> m;
for(int i = 0; i < 256; i++){
bit[i] = RAND();
ch[i] = RAND();
}
for(int i = 1;i <= n; i++){
string s;
cin >> s;
int hs = 0;
for(int i = 0; i < l; i++) hs ^= bit[i]*ch[s[i]];
for(int i = 0; i < l; i++) v[i].push_back(hs ^ bit[i]*ch[s[i]]);
}
int ans = 0;
for(int i = 0; i < l; i++){
sort(v[i].begin(),v[i].end());
for(auto l = v[i].begin();l != v[i].end();){
int x = *l;
int now = 0;
while(l != v[i].end() && *l == x) l++,ans += now++;
}
}
cout << ans << endl;
return 0;
}
你怎么知道我随机化函数是抄的