HDU 3613 Best Reward(扩展KMP)
【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=3613
【题目大意】
一个字符串的价值定义为,当它是一个回文串的时候,价值为每个字符的价值的和,如果不是回文串,价值为0,现在给出每种字符的价值。给出一个字符串,要求将其划分为两个子串,要求两个子串的价值和最大。
【题解】
求出字符串S的反串T,以T为模板跑一遍S的exkmp就能得到S的后缀是否为回文串的信息,同理以S为模板跑一遍T就可以得到S的前缀是否是回文串的信息,枚举每个断点,取最大值即可。
【代码】
#include <cstdio> #include <cstring> using namespace std; const int N=500010; int len,LCP[N],ex1[N],ex2[N],a[N],s[N]; char S[N],T[N]; void getLCP(char *T){ int i,len=strlen(T); LCP[0]=len; for(i=0;i<len-1&&T[i]==T[i+1];i++); LCP[1]=i; int a=1; for(int k=2;k<len;k++){ int p=a+LCP[a]-1,L=LCP[k-a]; if((k-1)+L>=p){ int j=(p-k+1)>0?(p-k+1):0; while(k+j<len&&T[k+j]==T[j])j++; LCP[k]=j,a=k; }else LCP[k]=L; } } void exkmp(char *S,char *T,int *extend){ memset(LCP,0,sizeof(LCP)); getLCP(T); int Slen=strlen(S),Tlen=strlen(T),a=0; int MinLen=Slen>Tlen?Tlen:Slen; while(a<MinLen&&S[a]==T[a])a++; extend[0]=a,a=0; for(int k=1;k<Slen;k++){ int p=a+extend[a]-1,L=LCP[k-a]; if((k-1)+L>=p){ int j=(p-k+1)>0?(p-k+1):0; while(k+j<Slen&&j<Tlen&&S[k+j]==T[j])j++; extend[k]=j;a=k; }else extend[k]=L; } } void revcpy(char* S,char* T,int len){ memset(T,0,sizeof(T)); for(int i=0,k=len-1;i<len;++i,--k)T[i]=S[k]; }int Cas; int main(){ scanf("%d",&Cas); while(Cas--){ for(int i=0;i<26;i++)scanf("%d",&a[i]); scanf("%s",S);len=strlen(S); for(int i=0;S[i];i++)s[i+1]=s[i]+a[S[i]-'a']; revcpy(S,T,len); exkmp(S,T,ex2); exkmp(T,S,ex1); int ans=-1e9; for(int i=0;i<len;i++){ if(i&&ex1[i]+i==len){ int j=ex1[i],tmp=s[j]; if(ex2[j]+j==len)tmp+=s[len]-s[j]; if(tmp>ans)ans=tmp; }else{ int j=i+1,tmp=0; if(ex2[j]+j==len)tmp+=s[len]-s[j]; if(tmp>ans)ans=tmp; } }printf("%d\n",ans); }return 0; }