给两个字符串s1
和s2
,如果s2
中包含s1
的某个全排列,则返回true
,否则返回false
。
最直观的解法就是:
- 枚举
s1
的所有全排列。递归算法的时间复杂度为递归次数*单次递归的复杂度
,递归树上每个非叶结点都是一次递归,树上共有\(O(n!)\)级别数目的非叶结点,因此递归调用次数为\(O(n!)\)。单次递归的复杂度为\(O(n)\)。因此这一步的时间复杂度为\(O(n\times n!)\)。递归调用的深度为\(O(n)\),即空间复杂度\(O(n)\)。 - 对于每个全排列,check其是否为
s2
的子串。如果用KMP算法,时间复杂度为\(O((n_1+n_2)\times n_1!)\),空间复杂度为\(O(n_1)\)。
综上,总的时间复杂度为\(O((2n_1+n_2)\times n_1!)\),总的空间复杂度为\(O(n_1)\)。
Approach 1
我拿到此题的第一感觉是哈希表,首先统计s1
中每种字符的频率,如果s2
中存在一个长度为\(n_1\)的连续区间窗口,完全符合s1
的字符频率表,则返回true
。
如果当前窗口不符合要求,向右滑动,更新移除和加入窗口的字符频率,继续与s1
的字符频率比对,直到s2
结尾。
1 | class Solution: |
比对2个哈希表的时间复杂度取决于不同字符的数目,这里为26,因此总的时间复杂度为\(O(26\times (n_2-n_1))\),空间复杂度为\(O(1)\)。
Approach 2
方法一用到了2个哈希表,m1
固定不变,m2
随窗口的滑动而变化,并与m1
对比每个字符的频率是否相等。因此可以转换思路,将对比每个字符的频率是否相等转换为:s1
中每个字符的频率与s2
窗口中每个字符频率的差值,只需要1个哈希表来记录s1
与s2
的字符频率差值即可。
先统计s1
的字符频率表m
,对于遍历到的s2
的每个字符s2[r]
:m[s2[r]]--
- 若
m[s2[r]]<0
,表明当前字符在s1
中未曾出现,或者当前在s2
窗口中的出现次数已经超过了在s1
中的出现次数,说明窗口[l,r]
中包含的字符太多,超出了s1
的要求,此时我们移动窗口的左边界l++
并更新m
。如果移动后[l,r]
中包含的字符仍然过多,继续l++
并更新m
,直到m[s2[r]]=0
。 - 若
m[s2[r]]=0
,表明该字符在当前窗口内的频率与s1
相同,且[l,r]
内的字符频率都与s1
相同,即m[s2[l...r]]=0
,如果此时窗口大小正好等于\(n_1\),返回true
。 - 若
m[s2[r]]>0
,表明[l,r]
内的字符不足以covers1
,移动窗口的右边界r++
。
1 | class Solution: |