[CTSC2014] 企鹅 QQ——哈希

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

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

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

输出格式

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

样例 #1

样例输入 #1

4 3 64
Fax
fax
max
mac

样例输出 #1

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\)

分析

注意到符合题意的字符串去掉不同的一位后所形成的字符串是相同的。

对于每个字符串,计算其去掉某一位后所形成的字符串的哈希值。

这样会产生至多\(N*L\)个哈希值,排序后可输出答案。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100,INF=0x7fffffff;
typedef unsigned long long ull;
ull key=137,bas[N];
char s[30010];
int n,len,siz,cnt;
ull all[6000100],h[30010];
void init()
{
    scanf("%d%d%d",&n,&len,&siz);bas[0]=1;
    for(int i=1;i<=len;++i)
        bas[i]=bas[i-1]*key;
    ull pre,bak;
    for(int i=1;i<=n;++i)
    {
        scanf("%s",s+1);
        for(int j=1;j<=len;++j)
        {
            h[j]=0;
            h[j]=h[j-1]*key+s[j];
        }
        for(int j=1;j<=len;++j)
        {
            //1~j-1
            pre=h[j-1];
            //j+1~len
            bak=(h[len]-h[j]*bas[len-j])*bas[j];
            all[++cnt]=pre+bak;
        }
    }
    sort(all+1,all+1+cnt);
    int st=1,ans=0;
    for(int i=2;i<=cnt;++i)
    {
        if(all[i-1]==all[i])++st;
        else {ans+=st*(st-1)/2;st=1;}
    }
    ans+=st*(st-1)/2;cout<<ans;
}
int main()
{
    init();
    return 0;
}









posted @ 2024-10-13 16:25  Glowingfire  阅读(3)  评论(0编辑  收藏  举报