Google 面试题 10 | 最多有k个不同字符的最长子字符串

题目描述

给定一个字符串,找到最长的包含最多k个不同字符的子串,输出最长子串的长度即可。

Example:
给出字符串”eceba”,k = 2
输出答案3,最长包含最多2个不同字符的子串为”ece”。

官方题解(并没有看懂):
最暴力的做法是穷举所有可能的子串,一共有n^2级别个不同的子串,然后对于每一个子串统计有多少不同的字符,如果少于k个就更新答案。这是一个三重循环的穷举,复杂度是O(n^3)。聪明的读者肯定想到了第二重和第三重循环可以合并,因为之前的统计信息可以继续使用而不需要每一次重新统计。这样的话穷举子串的开始位置和结束位置,复杂度是O(n^2)。如果在面试时提出了这个算法,面试官首先会表示认同,然后希望你进行优化。我们来看看如何进行优化。

在统计的过程中我们可以发现,当穷举的开始位置改变时我们会选择重新统计,但其实当开始位置改变时,我们之前记录的大部分信息都是有用的,我们只需在原有的统计结果上稍作修改。我们在两层for循环的时候其实会发现我们内层的fo循环其实并不需要每次都再重复遍历。比如外层for循环指针i每次向前移动变成i+1的时候,内层for循环的j没必要每次都退回到i的位置,其实是可以保留在j的位置不变的。因为可以证明对于从i+1到j-1这些情况都不会出现重复的元素,所以我们只用考虑从i+1到j就可以了。

自己的代码:

/**
 *Author: xiaoran
 *Solution:
 *
 */
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<time.h>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<fstream>
#define LL long long
using namespace std;
const int MAXN=1000005;
int test1_algorithm(char *s,int k){//O(n^3)
    int len,m;
    len = strlen(s);
    m = 0;
    for(int i=0;i<len;i++){
        for(int j=i;j<len;j++){
            int p=0,a[26]={0};
            for(int t=i;t<=j;t++){
                a[s[t]-'a']++;
            }
            for(int t=0;t<26;t++){
                if(a[t]) p++;
            }
            if(p<=k) m=max(m,j-i+1);//因为是从大区间到小区间的遍历,第一次满足的子串就是结果
        }
    }
    return m;
}
int test2_algorithm(char *s,int k){//记录上次的信息
    int len,m,j,i;
    len = strlen(s);
    m = 0;
    for(i=0;i<len;i++){
        int p=0,a[26]={0};
        for(j=i;j<len;j++){
            a[s[j]-'a']=1;
            p=0;
            for(int t=0;t<26;t++){
                if(a[t]) p++;
            }
            if(p<=k){
                m = max(m,j-i+1);//去掉最后一个
            }
        }
    }
    return m;
}
int test3_algorithm(char *s,int k){//O(n)
    int a[26]={0};
    int m=0,len=strlen(s);
    int start,end;
    start=end=0;
    while(end<len){
        a[s[end++]-'a']++;
        int p=0;
        for(int i=0;i<26;i++){
            if(a[i]) p++;
        }
        if(p<=k){
            m = max(m,end-start);
        }
        if(p>k) a[s[start++]-'a']--;
    }
    return m;
}


int main()
{

    //freopen("E:/input.txt","r",stdin);
    //freopen("E:/output.txt","w",stdout);
    char s[]="aabcdefgggg";
    int k=3;
    cout<<test1_algorithm(s,k)<<endl;
    cout<<test2_algorithm(s,k)<<endl;
    cout<<test3_algorithm(s,k)<<endl;


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值