最小覆盖子串 O(m+n)

/*
最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

 

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
 

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。
示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
 

提示:

m == s.length
n == t.length
1 <= m, n <= 10^5
s 和 t 由英文字母组成
 

进阶:你能设计一个在 o(m+n) 时间内解决此问题的算法吗?
通过次数411,430提交次数911,195

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>

struct t_info {
	int times;
	bool flag;
};

char * minWindow(char * s, char * t)
{
	int len_s = strlen(s);
	int len_t = strlen(t);

	int *s_next_match = (int *)malloc(len_s * sizeof(int));
	struct t_info t_stats[128];
	
	int i, first = -1, start = -1, pre = 0, last = -1, match_count = 0;
	uint32_t min_len = ~0;

	memset(t_stats, 0, sizeof(t_stats));

	for (i = 0; i < len_t; i++) {
		t_stats[t[i]].flag = true;
		t_stats[t[i]].times++;
	}

	//go first to get one matched info
	for (i = 0; i < len_s; i++) {

		if (!t_stats[s[i]].flag) {
			continue;
		}

		//if match in t-string
		if (first == -1) {
			first = i;
			last = i;
		}

		s_next_match[pre] = i;
		pre = i;

		t_stats[s[i]].times--;
		if (t_stats[s[i]].times >= 0) {
			match_count++;
		}

		if (match_count == len_t) {
			last = i;
			int len = last - first + 1;
			if (min_len > len) {
				min_len = len;
				start = first;
			}

			if (min_len == 1) {
				break;
			}
			//let it has one chance to check length
			while (first < last) {
				char ch_matched = s[first];
				first = s_next_match[first]; //to next matched
		
				//pop the first matched char
				t_stats[ch_matched].times++;
				if (t_stats[ch_matched].times <= 0) { //if matched in the strings
					int len = last - first + 1;
					if (min_len > len) {
						min_len = len;
						start = first;
					}
				} else {
					match_count--; //if not matched any more, go forward
					break;
				}
			}
		}
	}

	free(s_next_match);

	char *ret = NULL;
	if (start != -1) {
		ret = (char *)malloc((min_len + 1) * sizeof(char));
		strncpy(ret, s + start, min_len * sizeof(char));
		ret[min_len] = '\0';
	} else {
		ret = (char *)malloc(1);
		ret[0] = '\0';
	}

	return ret;
}

int main(int argc, char *argv[])
{
	if (argc != 3) {
		printf("To find min-window of two strings, please input two strings!\n");
		return 0;
	}

	char *ret =  minWindow(argv[1], argv[2]);
	printf("%s\n", ret ? ret : "");
    free(ret);

	return 0;
}

这个即便是有while 循环,它的内部之多是 O(2M), 所以总体还是O(M+N)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值