438. 找到字符串中所有字母异位词
题意:
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指字母相同,但排列不同的字符串。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
第一种解题思路:
我做了两种解法: 第一种是用STL做的,需用到个数来做标记当前字符串是否符合题意得需要,大概思想为:
- 先获得p字符串的个数count.
- 如果当前下标的字符在p字符串当中.进入循环,循环的次数为p的大小
- flag标记p的个数(不用count是因为循环体需要,每次都刷新flag),在循环过程中每一次都将flag–
- 循环完成时判断flag的个数.如果为0说明当前下标符合要求,就加入到结果容器中.
- 如果当前下标符合,不需要每一个字符都全部判断它是否全部一样.我只需要判断下一个字符和我删除的字符是否是一致的即可(重点想法).
这是我第一次想到的方法,但是时间复杂度太高,运行后通过60/60个案例却超时了(我也不明白为什么),所有后面我改进了方法.
第一种解法(超时):
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if(s.size() < p.size() || s.empty())
return {};
map<char, int> hash;
int count = p.size();
for (const char c : p)
{
++hash[c];
}
vector<int> res;
for (int i = 0; i < s.size() - count + 1; i++)
{
if (hash[s[i]] != 0)
{
map<char, int> t = hash;
int flag = count;
for (int j = 0; j < p.size(); j++)
{
if (t[s[i + j]] != 0)
{
--t[s[i + j]];
flag--;
continue;
}
break;
}
if (flag == 0)
{
res.push_back(i);
int index = 1;
while (i + count - 1 + index < s.size() && s[i] == s[i + count - 1 + index])
{
res.push_back(i + index);
i = i + index;
}
}
}
}
return res;
}
};
运行结果:
第二种解题思路:
第二种就是在第一种的想法上改进的.
- 第一点就是用数组替代了map容器,静态数组时间更加快
- 第二点是沿用动态窗口的想法,如果加进来的字符是所需要的字符,那么我就让count–,如果count==0,就说明当前下标符合要求
- 把左边的字符去除,如果左边的字符是所需要的字符,那么就让count++,这样就可以一直用count来判断当前下标是否符合要求.
- 这样就只需要遍历一次字符串即可,时间复杂度大大降低.
第二种解法:
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if (s.size() < p.size() || s.empty())
return {};
int hash[26] = { 0 };
int map[26] = { 0 };
int count = p.size();
for (const char c : p)
{
++hash[c - 97];
}
vector<int> res;
int right = 0;
int left = 0;
while (right < s.size())
{
while (right - left <= p.size() - 1)
{
++map[s[right] - 97];
if (map[s[right] - 97] <= hash[s[right] - 97])
count--;
right++;
}
if (count == 0)
{
res.push_back(left);
}
--map[s[left] - 97];
if (map[s[left] - 97] < hash[s[left] - 97])
count++;
left++;
}
return res;
}
};
运行结果:
总结:
想这道题想了好久,其实在第一种情况已经是改进版的,第一次时没有判断下一个字符和左边要出去的字符是否相等,这样的时间复杂度更大,所有没有通过60个案例就结束了.第一种情况改了很久之后,发现通过了60个案例还超时了就有点离谱.说明想法不行,所有得重新来过,直到隔天早上想到如果类似于滑动窗口可能会更加简单,所有就尝试做了,第一次是用map做的,感觉时间没必要所有后来改为数组,时间快了很多.做完之后很兴奋(毕竟改了好久).时间上也挺满意.