今天做了两道string类型的题,第一道是比较简单的就不贴出来了
第二道题:
传送门:
题目的大概意思是 给一个字符串S(由ACST四个字母组成),通过子串替换来使得S中ACST的个数相同,求得替换子串的最小长度
如GAAATAAA,用GTTCC来替换AAATA即可变成GGTTCCAA符合条件,结果是5;
题目最基本的思路是:从字符串中任意取出一个子串,只要剩下字串中ACST的个数都小于n/4即可。但如果有一个比n/4要大就不能通过替换成功。
两个指针i和j,一个表示子串的开头一个表示子串的结尾。
一开始想的是i从0-n,j从i-n;j每移动一次,保存ACST个数的数组相应减-,直到符合要求为止,结果=i-j+1,最小的结果即是题目结果。
但是这样的时间复杂度是o(n*n),最终也超时了
看了别人的代码,发现实在是太厉害了!
先贴上核心代码:
for(int i=0;i<n;i++) { while(j<n&&!isfinished()) { temp[m[s[j]]]--; j++; } if(isfinished()) { int x=j-i; if(result>x) result=x; } temp[m[s[i]]]++; }
这样之后j不需要每一次都从i到n,而是只有一次o-n即可
比如字符串GAAATAAA 初始: temp['G']=1 temp['A']=6 temp['T']=1
第一次循环结束 temp['G']=0;temp['A']=2;temp['T']=0 i=0;j=6;
然后不需要i=1,j=1重新,而是另temp[s[i]]++,也就是把之前去掉的子串头重新加入,j的值继续往后走
第二次循环开始时:temp['G']=1;temp['A']=2;temp['T']=0 相当于去掉了从1到5的子串。
全部代码:
#include <iostream> #include <cstdio> #include <string> #include <map> using namespace std; int temp[4]={0}; map<char,int> m; char s[500005]; int n; bool isfinished() { for(int i=0;i<4;i++) { if(temp[i]>n/4) return false; } return true; } int main() { //freopen("a.txt","rw",stdin); int result=10000000; m['A']=0; m['C']=1; m['T']=2; m['G']=3; scanf("%d",&n); scanf("%s",&s); for(int i=0;i<n;i++) { temp[m[s[i]]]++; } if(isfinished()) { printf("0 "); return 0; } else{ int j=0; for(int i=0;i<n;i++) { while(j<n&&!isfinished()) { temp[m[s[j]]]--; j++; } if(isfinished()) { int x=j-i; if(result>x) result=x; } temp[m[s[i]]]++; } } printf("%d ",result); return 0; }