Luogu P4503 [CTSC2014]企鹅QQ(字符串哈希)
题面
题目背景
\(PenguinQQ\) 是中国最大、最具影响力的 \(SNS(Social Networking Services)\) 网站,以实名制为基础,为用户提供日志、群、即时通讯、相册、集市等丰富强大的互联网功能体验,满足用户对社交、资讯、娱乐、交易等多方面的需求。
题目描述
小 \(Q\) 是 \(PenguinQQ\) 网站的管理员,他最近在进行一项有趣的研究——哪些账户是同一个人注册的。经过长时间的分析,小 \(Q\) 发现同一个人注册的账户名称总是很相似的,例如 \(Penguin1, Penguin2, Penguin3……\) 于是小 \(Q\) 决定先对这种相似的情形进行统计。
小 \(Q\) 定义,若两个账户名称是相似的,当且仅当这两个字符串等长且恰好只有一位不同。例如 \(“Penguin1”\) 和 \(“Penguin2”\) 是相似的,但 \(“Penguin1”\) 和 \(“2Penguin”\) 不是相似的。而小 \(Q\) 想知道,在给定的 \(n\) 个账户名称中,有多少对是相似的。
为了简化你的工作,小 \(Q\) 给你的 \(N\) 个字符串长度均等于 \(L\) ,且只包含大小写字母、数字、下划线以及 \('@'\) 共 \(64\) 种字符,而且不存在两个相同的账户名称。
输入输出格式
输入格式:
第一行包含三个正整数 \(N, L, S\) 。其中 \(N\) 表示账户名称数量, \(L\) 表示账户名称长度, \(S\) 用来表示字符集规模大小,它的值只可能为 \(2\) 或 \(64\) 。
若 \(S\) 等于 \(2\) ,账户名称中只包含字符 \(‘0’\) 和 \(‘1’\) 共 \(2\) 种字符;
若 \(S\) 等于 \(64\) ,账户名称中可能包含大小写字母、数字、下划线以及 \('@'\) 共 \(64\) 种字符。
随后 \(N\) 行,每行一个长度为 \(L\) 的字符串,用来描述一个账户名称。数据保证 \(N\) 个字符串是两两不同的。
输出格式:
仅一行一个正整数,表示共有多少对相似的账户名称。
输入输出样例
输入样例:
4 3 64
Fax
fax
max
mac
输出样例:
4
说明
\(4\) 对相似的字符串分别为: \(Fax\) 与 \(fax\) , \(Fax\) 与 \(max\) , \(fax\) 与 \(max\) , \(max\) 与 \(mac\) 。
测试点编号 | N | L | S |
---|---|---|---|
1 | 50 | 10 | 64 |
2 | 500 | 100 | 64 |
3 | 3000 | 100 | 2 |
4 | 3000 | 100 | 64 |
5 | 30000 | 50 | 2 |
6 | 30000 | 50 | 64 |
7 | 30000 | 200 | 2 |
8 | 30000 | 200 | 64 |
9 | 30000 | 200 | 2 |
10 | 30000 | 200 | 64 |
思路
\(Hash\) 是真的强,比 \(kmp\) 好玩多了。 --Uranus
那肯定啦。 --alecli
这是一道字符串哈希的题。考虑如果题目问的不是“相似字符串”而是“相同字符串”,那么可以直接用字符串哈希来解决这一问题。而既然所有字符串的长度相同,考虑枚举相似字符串的不同位,把它去掉,然后比较剩下的字符串是否相同就好了。
利用字符串哈希的话,只需要求一遍所有字符串前缀的哈希值,再反着跑一遍,求后缀的哈希值,每次合并前缀和后缀就好了。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const ULL Pa=131;
const ULL Pb=137;
ULL n,l,s,ans,aHash[30005][205],bHash[30005][205],tmp[30005];
string str;
int main()
{
cin>>n>>l>>s;
for(ULL i=0;i<n;i++)
{
cin>>str;
str='0'+str;
for(ULL j=1;j<=l;j++) aHash[i][j]=aHash[i][j-1]*Pa+str[j];
for(ULL j=l;j;j--) bHash[i][j]=bHash[i][j+1]*Pb+str[j];
}
for(ULL i=1;i<=l;i++)
{
for(ULL j=0;j<n;j++) tmp[j]=aHash[j][i-1]*139+bHash[j][i+1]*149;
sort(tmp,tmp+n);
int now=0;
for(ULL j=0;j<n-1;j++)
if(tmp[j]==tmp[j+1]) ans+=(++now);
else now=0;
}
cout<<ans;
return 0;
}