2015藍橋杯——密文搜索

題目描述:

標題:密文搜索

福爾摩斯從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;
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章