题目大意:为了分辨那些qq是一个人的小号,我们需要写一套程序来判定哪些名称是相似的。相似的定义是有且只有一个位置的字符不同。
思路:数据范围不算太大,很明显的Hash,二分都不用。听老师说今年去CTSC考试的学长中有一个人没AC这个题是因为想多了。他当时写了Hash,然后闲的没事自己出一组数据卡掉了自己的hash,然后就不敢交hash了,最后交了一个Trie树,结果T了。。血的教训告诉我们,不要想太多。。
先预处理每个字符串,得到hash值。然后枚举每一位,在O(1)的时间中吧这一位去掉的Hash取出来,然后判重。本来我想手写哈希表来着,看了网上的题解之后发现果断快排就能过。。。
CODE:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 30010
#define BASE 137
using namespace std;
int points,length;
char src[MAX][210];
unsigned long long hash[MAX][210],power[MAX];
unsigned long long _hash[MAX];
void Pretreatment()
{
power[0] = 1;
for(int i = 1; i <= length; ++i)
power[i] = power[i - 1] * BASE;
}
inline void GetHash(char *s,unsigned long long hash[])
{
for(int i = 1; i <= length; ++i)
hash[i] = hash[i - 1] * BASE + s[i];
}
inline unsigned long long CalcHash(unsigned long long hash[],int i)
{
unsigned long long re = hash[length];
re = re - hash[i] * power[length - i] + hash[i - 1] * power[length - i + 1];
return re;
}
int main()
{
scanf("%d%d%*d",&points,&length);
Pretreatment();
for(int i = 1; i <= points; ++i) {
scanf("%s",src[i] + 1);
GetHash(src[i],hash[i]);
}
int ans = 0;
for(int i = 1; i <= length; ++i) {
for(int j = 1; j <= points; ++j)
_hash[j] = CalcHash(hash[j],i);
sort(_hash + 1,_hash + points + 1);
_hash[points + 1] = 0;
int start = 1;
for(int j = 1; j <= points + 1; ++j)
if(_hash[j] != _hash[start]) {
int cnt = j - start;
ans += cnt * (cnt - 1) >> 1;
start = j;
}
}
cout << ans << endl;
return 0;
}