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; i < n; ++i)
ans += m[SS[i]];
printf("%d\n", ans-n);
return 0;
}
2018.2.12 新生訓練Week4 D.斐波那契數列
有一個數列 ,其中 。
給你一個數字,問他是這個數列的第幾項。
每行包括數列中的一項 (k≤100000)。
總行數 T≤100。
說明
看到標題以爲是很水的題……
實際上,斐波那契數列的100000項是一個超出long long範圍的數,因此一開始考慮用高精度。後來發現數據加強了,時限和內存又比較緊(卡掉了我的python預處理算法&&python滾動暴力算法&&C++高精度+二分查找算法),只能使用一些技巧。
類似hash,用一個大質數(比如19260817)將斐波那契數列的各項hash掉,再利用同餘定理查找答案,就做完了……?
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int p = 19260817;
int now, f[maxn];
map<int, int> m;
string s;
int main()
{
f[0] = f[1] = 1;
m[1] = 1;
for (int i = 2; i < maxn; ++i)
{
f[i] = (f[i-1] + f[i-2]) % p;
m[f[i]] = i;
}
while (cin >> s)
{
now = 0;
for (auto &i: s)
now = (now*10 + i-'0') % p;
printf("%d\n", m[now]);
}
return 0;
}