题意:
给出一个全由小写字母组成的字符串,让你找出三个区间,这三个区间不能重合,并且每个区间内1,每个字母出现的顺序不能超过m次,找出使得这三个区间长度之和最大的情况
题解:
1,如何找出最长的一个区间使得每个字母出现的次数不超过m次
用一个数组记录26个字母分别出现多少次,再用一个指针记录在当前位置下,在保证每个字母出现次数都不超过m的条件下,区间往左最长能有多长
当遍历到第i位,某个字母出现了m+1次时,记此时上述指针指向的位置是k,则令endp[k]=i-1,然后k++
i遍历完成整个字符串后,令当前k右边所有字符的right_broad都为字符串最末一个字符的位置
endp[i]-i+1中的最大值就是最长的区间
2,如何找出三个区间使得总的区间和最大
从后向前dp
第一次dp寻找在仅保留第i位及其右边的字符的情况下,最长的符合条件的区间
dp1[i]=max(dp1[i+1],endp[i]-i+1);
第二次dp寻找在仅保留第i位及其右边的字符的情况下,两个符合条件的区间的长度和的最大值
dp2[i]=max(dp2[i+1],endp[i]-i+1+dp1[endp[i]+1]);
这个状态转移公式解释一下,从第i位起的那个符合条件的区间要全取走,然后就剩下了从第endp[i]+1位起右边的剩余字符了,这时候dp1里面存储的就是答案
第三次dp同理
dp3[i]=max(dp3[i+1],endp[i]-i+1+dp2[endp[i]+1]);
#include<iostream> #include<string> #include<queue> /*struct Point{ int id; char c; } Point point(int a,char b){ Point tmp; tmp.id=a; tmp.c=b; return tmp; };*/ using namespace std; int alphabet[200]; int endp[10000007]; int dp1[10000007],dp2[10000007],dp3[10000007]; string s; int main(){ int n,m; cin>>n>>m; cin>>s; int l=n; queue<int> que[200]; for(int i=0;i<l;i++){ if(alphabet[s[i]]==m){ endp[que[s[i]].front()]=i-1; alphabet[s[i]]--; que[s[i]].pop(); } alphabet[s[i]]++; que[s[i]].push(i); } for(int i='a';i<='z';i++){ while(!que[i].empty()){ endp[que[i].front()]=l-1; que[i].pop(); } } for(int i=l-2;i>=0;i--){ endp[i]=min(endp[i],endp[i+1]); } /*for(int i=0;i<l;i++){ cout<<endp[i]<<" "; } cout<<endl;*/ //int dp1=l-1,dp2=l-1,dp3=l-1; for(int i=l-1;i>=0;i--){ dp1[i]=max(dp1[i+1],endp[i]-i+1); } for(int i=l-1;i>=0;i--){ dp2[i]=max(dp2[i+1],endp[i]-i+1+dp1[endp[i]+1]); } for(int i=l-1;i>=0;i--){ dp3[i]=max(dp3[i+1],endp[i]-i+1+dp2[endp[i]+1]); } cout<<dp3[0]<<endl; return 0; }