题目
题目又叫字符串,但是这道题是真正的关于字符串的题
思路
这道题可行的我能想出来的思路有两个,一个是二分,先猜测一个答案,然后验证这个答案,但是由于这种方法时间复杂度不如另一种方法:双指针(滑动窗口),所以这里不做记录,只记录双指针的方法:
设置两个指针,一个指针 j j j 标记符合题意的最短字符串的开始,另一个指针 i i i 标记字符串结尾(都是能取到的,闭区间),所以字符串长度就是 i − j + 1 i-j+1 i−j+1,初始时两个指针都在字符串开始处 0 0 0,然后让指针 i i i 往前走,直到找出一个符合题意的字符串,此时这个指针标记着字符串的结尾,然后指针 j j j 也开始走,直到走到第一个不符合题意的位置(注意指针所在位置开始的字符串仍然是符合题意的),然后更新答案的值。如此重复即可。
怎么表示一个子串是否符合题意呢?
使用一个哈希表(小写字母常用数组代替哈希表)记录每个字符出现的次数即可,由指针 i i i 遍历到的字符次数加 1 1 1,由指针 j j j 遍历到的字符的次数减 1 1 1(前提是减少之后不为 0 0 0)
代码
#include <stdio.h>
#include <string.h>
#define N 1000000 + 5
#define M 26
/**
* @brief 判断当前子串是不是符合题意的
*
* @param st
* @param len
* @return int
*/
int check(int* st, int len) {
for (int i = 0; i < len; i++) {
if (!st[i]) return 0;
}
return 1;
}
/**
* @brief 计算符合题意的子串最小长度
*
* @param s
* @return int
*/
int solve(const char* s) {
int i = 0, j = 0, ans = N;
int st[M] = {0};
while (s[i]) {
st[s[i] - 'a']++;
if (check(st, M)) {
while (j < i) {
// 注意只有当移动了 j 之后还符合题意才移动
if (st[s[j] - 'a'] <= 1) {
break;
}
st[s[j] - 'a']--;
j++;
}
if (i - j + 1 < ans) {
ans = i - j + 1;
}
}
i++;
}
return ans;
}
int main(void) {
char s[N];
scanf("%s", s);
do {
if (strlen(s) < M) {
printf("-114514\n");
break;
}
printf("%d\n", solve(s));
} while (0);
return 0;
}