通过示例我们可以看出,只要子串内包含第二个字符串的所有字符,它就是一个合法的子字符串
1.列出所有的子串,再从子串中一一查找
class Solution {
public:
long long validSubstringCount(string word1, string word2) {
long long n=word1.size();
long long m=word2.size();
long long character[26]={0}; //统计word2中的字符出现的次数
long long result=0;
for(int i=0;i<m;i++){
character[word2[i]-'a']++;
}
//使用前缀和数组统计到index为止,所有字符出现的次数
long long presum[n][26];
memset(presum, 0, sizeof(presum));
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
presum[j][word1[i]-'a']++;
}
}
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){//统计子串为i到j的字符
bool flag=true;
for(int k=0;k<26;k++){
if(i>0){
if(presum[j][k]-presum[i-1][k]<character[k]){
flag=false;
break;
}
}
else{
if(presum[j][k]<character[k]){
flag=false;
break;
}
}
}
if(flag) result++;
}
}
return result;
}
};
代码的复杂度到了O(n^3),不出意外的超时了。
2.滑动窗口
我们使用left,right作为左右边界
假设当前left 和right所框定的边界可以满足要求,那么我们把right往右移动后也一定可以满足要求,那么从字符串的右侧到right的所有字符串都应该被统计。此时我们再把窗口的左边界left往右移动,判断是否仍然满足。
如果当前left 和right所框定的边界不能满足要求,我们把right往右移,这样新加入字符才可能使得框定的边界满足要求
class Solution {
public:
bool Is_satisfied(long long (&source_character)[26],long long (&windows_character)[26]){
bool flag=true;
for(int i=0;i<26;i++){
if(windows_character[i]<source_character[i]){
flag=false;
break;
}
}
return flag;
}
long long validSubstringCount(string word1, string word2) {
int n=word1.size();
int m=word2.size();
long long source_character[26]={0};
long long windows_character[26]={0};
long long result=0;
for(int i=0;i<m;i++){
source_character[word2[i]-'a']++;
}
int left=0,right=-1;
while(right<n){
if(Is_satisfied(source_character,windows_character)){
result+=(n-right);//加上右窗口到原字符串右边界的所有字符串
windows_character[word1[left]-'a']--;//减去左窗口的字符
left++;//左窗口右移
}
else{
right++;
if(right>= n) break;
windows_character[word1[right]-'a']++;
}
}
return result;
}
};