2018.2.5 新生训练Week3 E.密码碰撞
EOJ 的登录系统爆出了一个重大问题,当正确的密码是你输入的密码的子串时,就可以成功登录!
例如你的密码是 abc,则你输入 abcc,aabc,甚至 dfjklsdfabcsdjfkl,都可以成功登录!
出现了这么大的问题,那就一定要有人来背锅,管理员们希望在背锅之前先衡量一下锅的大小。
现在有一份 EOJ 用户的密码表,里面包含了 n 个用户的密码,第 i 个用户的密码是 pwdi。我们定义锅的大小为所有有序对 (i,j) (i≠j) 的数量,使得用户 i 能够输入他的密码 pwdi 成功登陆用户 j 的账户。
换句话说,我们现在需要知道,有多少有序对 (i,j) (i≠j) 使得 pwdj 是 pwdi 的子串。
第 1 行包含一个整数 n,1≤n≤20 000,表示密码表中密码的数量。
第 1+i (1≤i≤n) 行包含一个长度不超过 10 且由小写字母组成的字符串,表示 pwdi。
说明
因为长度太短了,所以可以直接枚举子串(每个密码最多55个子串),hash一下存进map里统计子串的出现次数。然后对于每个密码,计算其在子串中出现的次数。记得要减去n,因为每个密码也一定是自己的子串。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20005;
typedef long long ll;
ll SS[maxn];
set<ll> S[maxn];
map<ll, int> m;
char s[15];
int n, ans, len;
int main()
{
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%s", s);
len = strlen(s);
for (int j = 0; j < len; ++j)
{
ll h = 0;
for (int k = j; k < len; ++k)
{
h = h * 29 + s[k]-'a'+1;
S[i].insert(h);
if (!j && k+1 == len) SS[i] = h;
}
}
for (auto x: S[i]) ++m[x];
}
for (int i = 0<