题意:字母表的26个字母都有一个价值,给定一个字符串,将该字符串切成两份,对于每一份,如果是回文串,就获得该子串的字母价值之和,否则该子串的价值为0。求出将字符串切成两份后能够获得的最大价值。
做法:先用Manacher算法求出以每个字母为中心的回文串的长度,并计算该字符串的前缀价值和。然后枚举切割点,得到两份子串。这样就可以知道每个子串的中心点,然后检查以该子串的中心点作为中心点的回文串的长度,如果长度等于该子串的长度,那么就加上该子串的价值。然后和最优价值比较就行了。
其实如果熟悉了Manacher算法的应用,这道题是很简单的。
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<stack> #include<vector> #include<queue> #include<string> #include<sstream> using namespace std; const int maxn=500005; char str[maxn],s[maxn<<1]; int p[maxn<<1],newlen; int val[26]; int presum[maxn]; void init(int n) { newlen=n<<1; for(int i=0;i<=newlen+1;i++) s[i]='#'; for(int i=1;i<=n;i++) s[i<<1]=str[i]; s[newlen+2]=0; } void manacher() { int mx=0,id=0; for(int i=1;i<=newlen;i++) { if(mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1; while(i-p[i]>=0&&s[i-p[i]]==s[i+p[i]]) p[i]++; if(i+p[i]>mx) { mx=i+p[i]; id=i; } } } int main() { int T; scanf("%d",&T); while(T--) { for(int i=0;i<26;i++) scanf("%d",&val[i]); scanf("%s",str+1); int len=strlen(str+1); presum[0]=0; for(int i=1;i<=len;i++) presum[i]=presum[i-1]+val[str[i]-'a']; init(len); manacher(); int ans=-(INT_MAX-1); for(int cutpnt=1;cutpnt<len;cutpnt++) { int temp=0; int len1=cutpnt,len2=len-len1; //看子串的长度的奇偶性 if(len1%2==1) { int midpnt=len1/2+1; int palen=p[midpnt*2]-1; if(midpnt-palen/2==1) temp+=presum[len1]; } else { int midpnt=len/2; int palen=p[midpnt*2+1]-1; if(midpnt-palen/2+1==1) temp+=presum[len1]; } if(len2%2==1) { int midpnt=len1+len2/2+1; int palen=p[midpnt*2]-1; if(midpnt+palen/2==len) temp+=presum[len]-presum[len1]; } else { int midpnt=len1+len2/2; int palen=p[midpnt*2+1]; if(midpnt+palen/2==len) temp+=presum[len]-presum[len1]; } if(temp>ans) ans=temp; } printf("%d\n",ans); } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-05 20:03:37