題目描述:
標題:密文搜索
福爾摩斯從X星收到一份資料,全部是小寫字母組成。 他的助手提供了另一份資料:許多長度爲8的密碼列表。
福爾摩斯發現,這些密碼是被打亂後隱藏在先前那份資料中的。
請你編寫一個程序,從第一份資料中搜索可能隱藏密碼的位置。要考慮密碼的所有排列可能性。
數據格式:
輸入第一行:一個字符串s,全部由小寫字母組成,長度小於1024*1024 緊接着一行是一個整數n,表示以下有n行密碼,1<=n<=1000
緊接着是n行字符串,都是小寫字母組成,長度都爲8
要求輸出: 一個整數, 表示每行密碼的所有排列在s中匹配次數的總和。
例如: 用戶輸入: aaaabbbbaabbcccc 2 aaaabbbb abcabccc
則程序應該輸出: 4
這是因爲:第一個密碼匹配了3次,第二個密碼匹配了1次,一共4次。
資源約定: 峯值內存消耗 < 512M CPU消耗 < 3000ms
請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入…” 的多餘內容。
所有代碼放在同一個源文件中,調試通過後,拷貝提交該源碼。
注意: main函數需要返回0 注意: 只使用ANSI C/ANSI C++ 標準,不要調用依賴於編譯環境或操作系統的特殊函數。 注意:
所有依賴的函數必須明確地在源文件中 #include , 不能通過工程設置而省略常用頭文件。
提交時,注意選擇所期望的編譯器類型。
思路1:
先對原串做一下預處理:對於原串中每8個連續的子串,存入二維數組中的一行中。然後對於子串,也是存入一行,之後與二維數組的每一行進行匹配,累加完全相同的。
其中,存每一行時,每一行開26個空間,每個空間對應一個小寫字母,記錄每個小寫字母出現的次數。
不過,這種暴力解法,複雜度達到了10^10,所有如果數據量過大,會超時應該,不過對於藍橋杯,應該可以拿一些分。
代碼:
#include <bits/stdc++.h>
#define maxn 11288576 + 50
using namespace std;
string s;
int n;
int cnt[maxn][30];
int rem[30];
int ans;
void init(){
int len = s.length();
for(int i = 0; i <= len-8; ++i){
for(int j = 0; j < 8; ++j){
char ch = s[i + j];
cnt[i][ch - 'a']++;
}
}
}
void calc(){
int len = s.length();
for(int i = 0; i <= len-8; ++i){
int flag = 1;
for(int j = 0; j < 26; ++j){
if(cnt[i][j] != rem[j]){
flag = 0;
break;
}
}
if(flag) ans++;
}
}
int main(){
cin >> s >> n;
init();
for(int t = 1; t <= n; ++t){
string tmp;
cin >> tmp;
memset(rem, 0, sizeof(rem));
for(int i = 0; i < 8; ++i){
char ch = tmp[i];
rem[ch - 'a']++;
}
calc();
}
cout << ans << endl;
return 0;
}
思路2:
仔細分析上面的代碼,發現主要的時間開銷來自於子串與原串進行匹配時,需要遍歷每一行,而原串的長度最大可達10^6,所以很費時。
既然發現是查找部分耗時嚴重,那麼就盡力去優化查找了,不難想到二分查找,不過由於二分查找,一般適用於數字,所以就想,能否將這由8個字母組成的子串轉爲數字,方法是:將這26個字母對應0~25,就可以將這個子串轉爲26機制數,最後再轉爲10進制即可。這樣就可以用二分進行查找了。
另外,不想手擼二分的話,可以直接用STL容器:set, map。這裏由於每個子串匹配的數量不僅僅只有1個,所以我用來map。
代碼:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
string s;
int n;
map<ll, int> mp;
int ans;
void init(){
int len = s.length();
for(int i = 0; i <= len - 8; ++i){
// 將原串中從i開始之後的8個字符存入向量中
vector<char> v;
for(int j = 0; j < 8; ++j){
char ch = s[i + j];
v.push_back(ch);
}
// 爲了和子串比較,先排序
sort(v.begin(), v.end());
// 將這個8個字符轉爲26進制數字,便於二分查找
ll sum = 0;
for(int j = 0; j < 8; ++j){
char ch = v[j];
sum = sum * 26 + (ch - 'a');
}
mp[sum]++;
}
}
int main(){
cin >> s >> n;
init();
for(int t = 1; t <= n; ++t){
string tmp;
cin >> tmp;
vector<char> v;
for(int i = 0; i < 8; ++i){
char ch = tmp[i];
v.push_back(ch);
}
// 爲了和原串比較,先排序
sort(v.begin(), v.end());
// 將這個8個字符轉爲26進制數字,便於二分查找
ll sum = 0;
for(int i = 0; i < 8; ++i){
char ch = v[i];
sum = sum * 26 + (ch - 'a');
}
ans += mp[sum];
}
cout << ans << endl;
return 0;
}