题意:由26个小写字母组成的字符串str,在str中查找最长但不含相同字符的连续子串。
1, 由于字符串只由小写字母组成,那么这个连续的字符串最长不会超过26。
2, 由于是连续的子串,那么很容易想到用一个滑动窗口来调整子串的长度,一旦出现重复的字母,那么需要缩小串口,没有出现重复那么可以继续放大。直到重复为止。
3, 现在重要的问题是怎么判定窗口字母是否重复,有两个办法:
a, 每新进一个字符,就返回去找窗口内的其他字符,看是否重合,如果重合,那么这时候计算窗口的大小,然后把窗口的左侧移动到重复字符的右边。
b, 利用一个计数器来记录窗口内的各个字符出现的次数,这样只要O(1)的时间就能知道当前这个字符是否出现过,如果出现了,那么字需要记录当前窗口大小,然后移动左边窗口,并把移出窗口的字符对应的计数器减1,直到当前字符不重复了。
总体来看,这整个算法的重点就在怎么判断一个字母在窗口中是否重复出现,而上面提供的两个算法在性能上差异不是太大,当然b方法要略微好于a方法,但是他们在算法复杂度上实际上都是O(N)的,只是系数有些差异。
下面代码实现了方法b
#include<stdio.h>
#include<string.h>
#include<memory.h>
int main( int argc, char **argv)
{
char s[1024];
int char_counter[26];
scanf("%s", s );
int max_length = 0;
int left = 0;
int right = 0;
memset(char_counter, 0, sizeof(char_counter));
while ( s[right] != 0)
{
if( char_counter[s[right] - 'a'] == 1 ) //出现重复的字母了
{
if ( max_length < right - left) //记录当前窗口大小
{
max_length = right - left;
}
while ( char_counter[s[right] - 'a'] == 1 ) //滑动串口左侧,直到字符没有重复
{
char_counter[s[left++] - 'a'] --;
}
}
char_counter[s[right++] - 'a'] ++;
}
if ( max_length < right - left)
{
max_length = right - left;
}
printf( "max length = %d\n", max_length );
return 0;
}