leetcode刷题/哈希表 438. 找到字符串中所有字母异位词

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做的,感觉时间没必要所有后来改为数组,时间快了很多.做完之后很兴奋(毕竟改了好久).时间上也挺满意.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公仔面i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值