描述
如果一个字符串恰好包含2个’h’、1个’i’和1个’o’,我们就称这个字符串是hiho字符串。
例如"oihateher"、"hugeinputhugeoutput"都是hiho字符串。
现在给定一个只包含小写字母的字符串S,小Hi想知道S的所有子串中,最短的hiho字符串是哪个。
输入
字符串S
对于80%的数据,S的长度不超过1000
对于100%的数据,S的长度不超过100000
输出
找到S的所有子串中,最短的hiho字符串是哪个,输出该子串的长度。如果S的子串中没有hiho字符串,输出-1。
思路
本题是一道比较常见的双指针(或滑动窗口)题目
首先值得注意的是题目要求找到S的子串“恰好”包含2个h、1个i和1个o,不是“至少”包含。
本题的思路就是枚举子串的起点位置L=0, 1, 2, … |S|-1, O(|S|); 然后找到一个最短的,也就是右端点R最小的、使得S[L…R]“至少”包含2个h、1个i和1个o。
然后判断S[L…R]是不是“恰好”包含2个h、1个i和1个o;如果是,我们就找到了一个答案,就用R-L+1更新当前最短的答案。
本题可以用双指针(或滑动窗口),奥妙就在于,当我们要枚举下一个起点L时,即令L=i变成L=i+1时,新的R(i+1)一定在R(i)的右边。
这样R不用从L+1开始计算,总的复杂度就是O(|S|)的了。
代码
#include <stdio.h>
#include <string.h>
#define LEN 100010
int main()
{
char str[LEN];
int i,j, minl = LEN;
scanf("%s", str);
int l = strlen(str);
int hn = 0, in = 0, on = 0; // hn > 2 in > 1 on > 1
for( i = 0, j = 0; i < l; i++)
{
if( str[i] != 'h' && str[i] != 'i' && str[i] != 'o' )
{
continue;
}
while( j < l && ( hn < 2 || in < 1 || on < 1 ))
{
switch(str[j])
{
case 'h': hn++; break;
case 'i': in++; break;
case 'o': on++; break;
}
j++;
}
if( hn == 2 && in == 1 && on == 1 )
{
minl = minl > (j - i) ? j - i : minl;
// printf("minl = %d, j = %d, i = %d\n", minl, j, i);
}
switch(str[i])
{
case 'h': hn--; break;
case 'i': in--; break;
case 'o': on--; break;
}
}
if( minl == LEN ) minl = -1;
printf("%d\n", minl);
return 0;
}