题意:给定一个长度不大于5000的字符串str和一个整数m,对于str的两个长度均为n且不相交的子串AB,定义dis(A,B)=∑(i=0,n-1)|A[i]-B[n-i-1]|,求dis(A,B)<=m时的最大n值。(m<=5000)
这种区间的变化有规律的问题,尺取是一个很好的算法。对于这道题,如果枚举了A串的右端点和B串的左端点,在A左端点左移和B右端点右移时,dis值单调递增或不变,故采用尺取,首先枚举A串的右端点a和B串的左端点b,然后预处理出s[k]=abs(str[a-k]-str[b+k]),然后直接对s进行尺取即可。
尺取法一般需要预处理出一些值,然后枚举左端点,不断推进右端点直至无法推进,然后更新答案即可。
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) typedef long long LL; using namespace std; char str[5003]; int s[5003]; int main() { int T,ans; scanf("%d",&T); while(T--) { int n,m; scanf("%d",&m); scanf("%s",str+1); n=strlen(str+1); ans=0; FOR(mid,1,n)FOR(extra,1,2) { //枚举中间点,分两类 int i=mid,j=mid+extra,cnt=0; while(1<=i&&j<=n) s[++cnt]=abs(str[i--]-str[j++]); int R=0,sum=0; FOR(L,1,cnt) //对s数组进行尺取 { while(R<cnt&&sum+s[R+1]<=m)sum+=s[++R]; ans=max(ans,R-L+1); if(cnt==R)break; sum-=s[L]; } } printf("%d\n",ans); } return 0; }