题目描述:
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo"
输出: False
注意:
输入的字符串只包含小写字母
两个字符串的长度都在 [1, 10,000] 之间
解答:
由于字符串的长度在[1,1000],所以如果考虑s1的排列情况,那实在太多了,用s2去对比s1的排列这个思路不太可行。
比较可行的思路是,可以计算s1的长度并统计s1串中每个字母的出现次数,然后去比较s2串中相同长度内字母出现次数是否相同。这里其实用到了滑动窗口的思想。
根据以上思路个人实现的代码如下:
class Solution {
public:
bool checkInclusion(string s1, string s2) {
int m1[256]={0}; //存放s1串中不同字母的个数
int m2[256]={0}; //存放s2串中不同字母的个数
int flag=0; //判断是否包含的标志位
if(s2.size()<s1.size()) //当s2字符串比s1字符串短时
return false; //直接返回false
else{
for(int i=0;i<s1.size();i++) //通过循环统计s1串中的字母个数
m1[s1[i]]++; //将字母的ASCII码值作为数组下标,遇到一次就加1
for(int j=0;j<s2.size()-s1.size()+1;j++){ //外层循环为每一个滑动窗格的起始元
素,循环次数为s2的长度-s1的长度再加1
memset(m2,0, sizeof(m2)); //每次循环前,将记录s2排列的数组清零
for(int k=j;k<s1.size()+j;k++){ //用此循环来统计每一个滑动窗口内出现的字母
个数
m2[s2[k]]++; //将字母的ASCII码值作为数组下标,遇到一次就加1
}
for(int n=0;n<256;n++){ //对数组进行遍历
if (m1[n] == 0 && m2[n] == 0) //当都是零的时候,说明两个字符串中均没有该字母
continue; //继续遍历
else if (m1[n]!=0 && m2[n]!=0 && m1[n] == m2[n]) { //当都不是零且数值相同
时,说明两个字符串中该字母出现的个数相同
flag = 0; //标志位为0
continue; //继续遍历
}
else //说明遍历过程中,出现了某个字母出现次数不一样的情况
{
flag = 1; //标志位变为1
break; //直接跳出此次遍历
}
}
if(flag==0) //标志位为0的时候,说明在遍历时字符串中字母出现的次数均相同
return true; //包含相同的排列,返回true
}
return false; //没有返回true,则返回false
}
}
};
以上代码虽然提交通过,但是运行时间为204ms,比较慢,仅仅战胜了19%的cpp提交记录。
改进:
所以之后又参考了leetcode官网上一些其他比较好的回答,发现可以从以下几个方面进行改进:
- 利用字典,减少存储空间,同时不需要遍历进行比较;
- 以s1字符串的长度,作为循环次数,同时进行统计,而不是分成两次分别去统计s1和s2;
- 边比较边修改频率,不需要每次清零重新统计;
在进行以上方面的改进后,代码如下:
class Solution {
public:
bool checkInclusion(string s1, string s2) {
vector <int> m1(26,0); //构建s1串的字典
vector <int> m2(26,0); //构建s2串的字典
if(s1.size()>s2.size()) //判断边界情况
return false;
int windowsize=s1.size(); //计算滑动窗口的大小
for(int i=0;i<windowsize;i++){ //统计初始滑动窗口中字母的排列
m1[s1[i]-'a']++;
m2[s2[i]-'a']++;
}
for(int i=windowsize;i<s2.size();i++){ //循环起始值为滑动窗格内的最后一位,最大值为s2
的长度
if(m1==m2)
return true;
m2[s2[i-windowsize]-'a']--; //滑出去的字母个数要减1
m2[s2[i]-'a']++; //滑进来的字母个数要加1
}
return m1==m2; //在上述循环条件下,少比较一次,所以增加一次判断
}
};
以上代码的执行时间明显就快了很多了。