1.解析
題目大意,判斷字符串s1(不考慮其順序)是否是s2的子串。
2.分析
這道題最簡單的辦法無非就是利用系統提供的next_permutation函數,該函數提供字符串排序的功能,這樣就可以不用考慮s1的順序,每次求解下一個排列,然後s2字符串直接判斷是否是其子串即可。這裏要特別注意next_permutation函數,如果不理解,可以具體查看它的源碼。但這種方法的時間複雜度很高,當s1字符串的長度超過20,進行全排序會耗掉大量的時間,更不用說100以上啦,所以肯定會TTL(時間超時)。
class Solution {
public:
bool checkInclusion(string s1, string s2) {
if (s1.size() > s2.length()) return false;
string org_s1 = s1;
while (true){
if (s2.find(s1) != string::npos) return true;
next_permutation(s1.begin(), s1.end()); //求解下一個排列
if (org_s1 == s1) break;
}
return false;
}
};
上述方法行不通,一般求解連續子串使用滑動窗口無非是最好的。我們可以維持一個n大小的窗口,首先用hashtable統計字符串s1每個字符出現的個數(方便查找),然後,遍歷s2字符串,主要分爲以下兩種情況:
①s2字符串當前的字符不屬於s1字符串:重置窗口大小爲n,並重置hashtable
②s2字符串當前的字符屬於s1字符串:(1)匹配字符的個數已經爲0:向右滑動窗口,恢復待匹配的字符個數,直到碰到當前匹配的字符 (2)匹配字符的個數 > 0:當前窗口大小減1,待匹配的字符減1
具體實現如下:
class Solution {
public:
bool checkInclusion(string s1, string s2){
unordered_map<char, int> ch_to_val, c_ch_to_val;
for (auto ch : s1) ch_to_val[ch]++;
c_ch_to_val = ch_to_val;
int n = s1.size(), m = s2.size(); //n爲滑動窗口大小
bool flag = true;
int start;
for (int i = 0; i < m; ++i){
if (ch_to_val.count(s2[i])){
if (ch_to_val[s2[i]] > 0){
if (flag){
start = i; //記錄起始位置
flag = false;
}
if (--n <= 0) return true;
ch_to_val[s2[i]]--;
}
else{
for (; s2[start] != s2[i]; ++start){
n++;
ch_to_val[s2[start]]++; //重新計數
}
start++;
}
}
else{ //重新匹配窗口
ch_to_val.clear();
ch_to_val = c_ch_to_val;
n = s1.size();
flag = true;
}
}
return false;
}
};
下面這種解法是對上面算法的優化,參考Grandyang博主的思路,其實我想複雜啦。用數組記錄s1字符串每個字符出現的個數,藉助左右指針,左指針代表窗口的左邊界,右指針代表窗口的右邊界。在遍歷字符串s2的過程中,如果當前字符在待匹配字符數組中,則判斷當前窗口的大小是否等於s1字符串的大小,如果等於,則滿足條件;若當前字符不在待匹配字符數組,則要麼是該字符不存在字符串s1要麼待匹配字符已經匹配完,則往左滑動窗口,並恢復之前匹配過的字符的個數,直到當前字符的計數爲0。之所以將數組大小設置爲123,主要是因爲字符'z'對應的ASCII爲122。具體實現如下:
class Solution {
public:
bool checkInclusion(string s1, string s2){
vector<int> val(123, 0);
int n = s1.size(), left = 0; //left是左邊界
for (auto ch : s1) val[ch]++;
for (int right = 0; right < s2.size(); ++right){ //右邊界
if (--val[s2[right]] < 0){
while (++val[s2[left++]] != 0);
}
else if (right - left + 1 == n) return true; //判斷窗口大小
}
return false;
}
};
下面這種解法也是來自Grandyang博主,使用雙數組,不借助雙指針。思路比較簡單,具體實現如下:
class Solution {
public:
bool checkInclusion(string s1, string s2){
vector<int> m1(123, 0), m2(123, 0);
int n1 = s1.size(), n2 = s2.size();
for (int i = 0; i < n1; ++i){
m1[s1[i]]++;
if (i < n2) m2[s2[i]]++;
}
if (m1 == m2) return true;
for (int i = n1; i < n2; ++i){
++m2[s2[i]];
--m2[s2[i-n1]];
if (m1 == m2) return true;
}
return false;
}
};