Google 面試題 10 | 最多有k個不同字元的最長子字串
阿新 • • 發佈:2019-02-05
題目描述
給定一個字串,找到最長的包含最多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;
}