HDU-6103 Kirinriki(尺取法)

题意:给定一个长度不大于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;
}

猜你喜欢

转载自blog.csdn.net/paulliant/article/details/80288813