一、题目
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s.
Strings consists of lowercase English letters only and the length of both stringss andp will not be larger than 20,100.
The order of output does not matter.
Example 1:
Input: s: "cbaebabacd" p: "abc" Output: [0, 6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc".
Example 2:
Input: s: "abab" p: "ab" Output: [0, 1, 2] Explanation: The substring with start index = 0 is "ab", which is an anagram of "ab". The substring with start index = 1 is "ba", which is an anagram of "ab". The substring with start index = 2 is "ab", which is an anagram of "ab".题意:给定两个字符串s,p,p是非空的。在s中查找p中所有字母一一对应,顺序可变。
字符串的题首先确定字符集,大小写,以及p的长度大于s的长度等异常情况。
注意:字符集的范围,有多解的情况返回索引的顺序,该题的范围是字母,默认是小写字母
思路:
暴力解,外围循环遍历s,内层以p的长度为窗口,从外围循环的元素开始一个一个的滑动。期间挨个比较p中元素是否都在窗口内并且一一对应。这样做运行超时
时间复杂度:O(n^2)
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int l=0,r=-1;
vector<int> index;
int freq[256]={0};
int flag = 1; //所有元素都一一对应的标志位
while(l<s.size())
{
flag = 1; //恢复,进行新的一轮循环
memset(freq,0,sizeof(freq));
for(int i=0;i<p.size();i++)
{
freq[p[i]]++;
}
for(r=l;r<p.size()+l;r++) //实际为窗口的大小也就是p字符串的个数。[l...size+l]
{
if(freq[s[r]]==0) //窗口内元素只要有一个不存在,跳出移动下一个位置
{
l++;
flag=0;
break;
}
freq[s[r]]--; //窗口内元素存在,当前元素的频率值-1
}
if(flag)
{
index.push_back(l);
l++;
}
}
return index;
}
};
别人的解法也是滑动窗口,暂时未看懂,待下次分析。
今早捋顺一下思路,leetcode一天不易刷太多,1,2题就好。对比和自己的思路差别,在进行窗口内元素和p一一对应关系采用了遍历,还需不断恢复p元素出现的频率,一下思路进行了很好地优化,直接比较。
思路:
1.首先将p内字符出现的频率记录到pv中。窗口大小一直是固定的为p的长度[0...p.size()-1]
2.从字符串s起始位置开始,设置一个窗口,看sv窗口内元素出现的频率是否和pv相等。相等就记录0位置
3.循环是从p.size开始,该位置i是窗口截止位置,将该位置i元素加入到窗口内统计其出现频率sv,因为窗口的大小是固定的右侧加了一个元素,左边i-p.size()就要向右移动一个元素到i-p.size()+1,并将sv更新。一句话,窗口整体向右滑动一位,看和pv是否相等,相等就将起始位置记录。窗口大小[i-p.size+1,i],起始位置就是:i-p.size()+1
//仅仅26字母版本,而且还是小写
//时间复杂度:O(n)
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> pv(26,0), sv(26,0), res;
if(s.size() < p.size())
return res;
// fill pv, vector of counters for pattern string and sv, vector of counters for the sliding window
for(int i = 0; i < p.size(); ++i)
{
++pv[p[i]-'a'];
++sv[s[i]-'a'];
}
if(pv == sv)
res.push_back(0);
//here window is moving from left to right across the string.
//window size is p.size(), so s.size()-p.size() moves are made
for(int i = p.size(); i < s.size(); ++i)
{
// window extends one step to the right. counter for s[i] is incremented
++sv[s[i]-'a'];
// since we added one element to the right,
// one element to the left should be forgotten.
//counter for s[i-p.size()] is decremented
--sv[s[i-p.size()]-'a'];
// if after move to the right the anagram can be composed,
// add new position of window's left point to the result
if(pv == sv)
res.push_back(i-p.size()+1);
}
return res;
}
};
当题目要求是ASCII字符集,可以使用如下代码。事实上,下边的代码可以使用在只有小写字母的情况下,只是效率有所降低
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> pv(256,0), sv(256,0), res;
if(s.size() < p.size())
return res;
for(int i = 0; i < p.size(); ++i)
{
++pv[p[i]];
++sv[s[i]];
}
if(pv == sv)
res.push_back(0);
for(int i = p.size(); i < s.size(); ++i)
{
++sv[s[i]];
--sv[s[i-p.size()]];
if(pv == sv)
res.push_back(i-p.size()+1);
}
return res;
}
};