1. 解析
題目大意,在字符串中查找給定的子序列,該子序列只要求出現的字符相同,而不要求順序一樣。
2. 分析
整體上,這道題還是比較容易想到的。每次在s串中切割長度和p相同的字符串,然後比較它們出現的字符是否相同,如果一樣,記錄起點的位置;如果不一樣,說明兩個子串不相等。將字符串往後移動,逐個切割判斷即可。這裏的關鍵是如果判斷兩個子串出現的字符是一樣的,我剛開始想到的解法是,分別將這兩個子串進行排序,這樣就可以直接比較了,但這種方法是過不了OJ的,因爲當子串p特別大的時候,調用系統的sort算法是特別耗時的。最好的做法無非就是利用數組進行存儲,因爲字符串都是由26個小寫組成,所以我們只需建立一個26個長度的序列就可以表示它們出現的情況。用數組的下標表示出現的字符,即'a'就是0,'b'就是1,......,以此類推。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int p_len = p.length();
vector<int> res;
if (s.length() < p_len) return res; //如果查找的子串的長度大於母串
vector<int> s_count(26, 0);
vector<int> p_count(26, 0);
for (int i = 0; i < p_len; ++i){ //記錄待查找字符串p的字符出現次數以及在字符串s中前p字符串長度的字符出現個數
++s_count[s[i]-'a'];
++p_count[p[i]-'a'];
}
if (s_count == p_count) res.push_back(0);
for (int i = p_len; i < s.length(); ++i){
--s_count[s[i-p_len]-'a'];
++s_count[s[i]-'a'];
if (s_count == p_count)
res.push_back(i-p_len+1);
}
return res;
}
};
滑動窗口解法:
left-----窗口的左邊界,right-------窗口的右邊界,cnt-----要匹配的字符總數,m------存儲字符串p中字符出現的個數
當字符串s中的某段字串出現的字符和p中出現的一樣時,cnt就會變成0,將左邊界left記錄下來;當窗口的大小大於設定的大小時,要將左邊界右移,同時判斷左邊界出現的字符是否在p中,如果存在,去掉後,意味着要匹配的字符個數要增加1個(關鍵的地方)。
class Solution {
public:
vector<int> findAnagrams(string s, string p){
vector<int> res;
vector<int> m(26, 0);
for (char ch : p) ++m[ch-'a']; //記錄p串中每個字符出現的次數
int left = 0, right = 0, len = s.length(), cnt = p.length();
while (right < len) { //維持的整個窗口大小爲子串p的長度
if (m[s[right++]-'a']-- >= 1) cnt--;
if (cnt == 0) res.push_back(left); //若當前p出現的字符都被匹配,則意味着出現相同的子串
if (right - left == p.length() && m[s[left++]-'a']++ >= 0) cnt++; //若當前窗口的大小比設定窗口大1,則左邊界往右移動
}
return res;
}
};