您的位置:首页 > 其它

hdu 6103 Kirinriki(多校联赛)

2017-08-10 18:41 399 查看


Kirinriki

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)


Problem Description

We define the distance of two strings A and B with same length n is
disA,B=∑i=0n−1|Ai−Bn−1−i|

The difference between the two characters is defined as the difference in ASCII.

You should find the maximum length of two non-overlapping substrings in given string S, and the distance between them are less then or equal to m.

 

Input

The first line of the input gives the number of test cases T; T test cases follow.

Each case begins with one line with one integers m : the limit distance of substring.

Then a string S follow.

Limits
T≤100
0≤m≤5000

Each character in the string is lowercase letter, 2≤|S|≤5000
∑|S|≤20000

 

Output

For each test case output one interge denotes the answer : the maximum length of the substring.

 

Sample Input

1
5
abcdefedcb

 

Sample Output

5

Hint[0, 4] abcde
[5, 9] fedcb
The distance between them is abs('a' - 'b') + abs('b' - 'c') + abs('c' - 'd') + abs('d' - 'e') + abs('e' - 'f') = 5

 

Source

2017 Multi-University Training Contest
- Team 6

题意:给出一个字符串 有两个长度相同长度的子串 根据题下的注释可以求出他们之间相应的差值 现在给你他们之间的差值 让你求出他们的最大的长度 当时比赛的时候看错了题意 以为是 给还粗他们之间的差值 让你求他们的最大 长度 认为没有巧妙的算法肯定会超时 后来百度题解才发现自己理解的题意都是错误的  

题解:百度上大致有两种方法 一种是尺取法 另一种则是动态规划的思想 具体看代码把 主要详细阐述下尺取法 感觉比较巧妙

ac代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=21000;
char str[maxn];
int m,len;
int solve()
{
int ans=0;
for(int i=len; i>=1; i--)//从字符串尾向头遍历所有的i
{
int cnt=i/2-1,d=0,t=0,p=0;//cnt是字符串长度的一半(以为每一个子串的长度不会大于主串长度的二分之一) d代表当前两个子串的差值和 t代表子串长度
for(int j=0; j<=cnt; j++)
{
d+=abs(str[1+j]-str[i-j]);//当前相应位上的字符差的和
if(d>m)//比较当前长度和题目限制
{//如果大于 则需要对子串进行减字符 减的字符长度为p
d-=abs(str[1+p]-str[i-p]);//减去内部标记长度的字符个数的差值
d-=abs(str[1+j]-str[i-j]);//减去本次循环所加上j上字符的差值 即退回到是上一个j是的状态
p++;//以p为当前的子串的内部的长度的标记 也是尺取法的巧妙之处
t--;//子串长度减少
j--;//相当与后退到上一位的j的状态  因为要求出当前j的最长子串 而减少的长度就用p的来确定
}
else//如果还没超出范围 就继续加长子串的长度
{
t++;
ans=max(ans,t);//遍历完所有的主串之后 取最大的子串的长度值
}
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int ans=0;
scanf("%d%s",&m,str+1);//输入规定的子串差值和字符串
len=strlen(str+1);//字符串长度
ans=max(ans,solve());
reverse(str+1,str+len+1);//将字符串反转然后再用函数求一遍所有的可能的ans值
//不知道为什么用这一步 希望大佬能够指出 删掉就wr了 感觉可能是因为函数遍历是j是小于len/2 所以要倒过来再遍历后面的半个主串
ans=max(ans,solve());
printf("%d\n",ans);
}
return 0;
}


法2:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int mx = 5e3+5,mod = 1e9+7;
int len[2][mx],dp[2][mx];
char str[mx+10];
int main()
{
int t,cas = 1;
scanf("%d",&t);
while(t--)
{
memset(len,0,sizeof(len));
memset(dp,0,sizeof(dp));
scanf("%d%s",&n,str+1);
int lens = strlen(str+1);
int id = 1, ans =0;
for(int i=lens;i>=1;i--,id ^= 1)
{
for(int j=i+1;j<=lens;j++)
{
int v = abs(str[j]-str[i]);
if(v>n)
{
dp[id][j] = 0,len[id][j] = v;
continue;
}
if(len[id^1][j-1]+v<=n)
dp[id][j] = dp[id^1][j-1] + 1,len[id][j] = len[id^1][j-1]+v;
else
{
int val = len[id^1][j-1], l = dp[id^1][j-1];
while(v+val>n&&l)
{
val -= abs(str[i+l]-str[j-l]);
l--;
}
dp[id][j] = l+1, len[id][j] = v+val>n? v:v+val;
}
ans = max(ans,dp[id][j]);
}
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: