【题解】P4503 [CTSC2014] 企鹅 QQ

题目传送门

[CTSC2014] 企鹅 QQ

题目背景

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,账户名称中只包含字符 01 2 2 2 种字符;

S S S 等于 64 64 64,账户名称中可能包含大小写字母、数字、下划线以及 @ 64 64 64 种字符。

随后 N N N 行,每行一个长度为 L L L 的字符串,用来描述一个账户名称。数据保证 N N N 个字符串是两两不同的。

输出格式

仅一行一个正整数,表示共有多少对相似的账户名称。

样例 #1

样例输入 #1

4 3 64
Fax
fax
max
mac

样例输出 #1

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 位了

对于每一位的哈希值也有很多种方法,随便套一种稍微复杂点的函数就行

Code

#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;
}

你怎么知道我随机化函数是抄的

你可能感兴趣的:(刷题笔记,c++,学习,字符串)