hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串
Posted 天道酬勤007
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串相关的知识,希望对你有一定的参考价值。
/** 题目:hdu3613 Best Reward 链接:http://acm.hdu.edu.cn/showproblem.php?pid=3613 题意:有一个字符串,把他切成两部分。 如果这部分是回文串,那么他的值为所有字母的权值和。否则这部分值为0;这两部分的值和为该切法的权值。 求最大的切法的权值。 思路: 如果能够判断[0,i],[i,n-1]是一个回文串(0<=i<n)那么就可以枚举i,计算切割位置为i时候两部分的贡献和。 取最大的。 利用O(n)的算法求最长回文子串的做法获得f[i];表示以第i个字符为中心的最长回文子串的长度; 就可以获得l[i],r[i]; l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。 */ #include <cstdio> #include <cstring> #include <algorithm> #include<set> #include <iostream> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 1e6+10;///给定的长度 char s[maxn*2], t[maxn];///要乘以2,因为构造了‘*‘ int f[maxn*2];///令f[i] 表示以第i个字符为中心的最长回文子串的长度 int sum[maxn*2], w[30]; int l[maxn*2], r[maxn*2];///l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。 int longest(char *a)///最长回文子串 { int z = 0; for(int i = 0; a[i]!=‘\0‘; i++){ s[z++] = a[i]; s[z++] = ‘*‘; } z--; s[z] = ‘\0‘; int j = 0; int ans = 1; f[0] = 1; for(int i = 1; i < z; i++){ if(f[j]-2*(i-j)<=0) { f[i] = 1; }else{ f[i] = min(f[2*j-i],f[j]-2*(i-j)); } int l = i-f[i]/2-1, r = i+f[i]/2+1; while(l>=0&&r<z&&s[l]==s[r]){ l--, r++; f[i]+=2; } if(f[i]/2+i>f[j]/2+j){ j = i; } if(f[i]>ans){ ans = f[i]; } } int mas = 0; for(int i = 0; i < z; i++){ if(f[i]==ans){ int l = i-f[i]/2, r = i+f[i]/2; int cnt; if(s[l]==‘*‘){ cnt = f[i]/2; }else cnt = f[i]/2+1; mas = max(mas,cnt); } } return mas; } int main() { //freopen("in.txt","r",stdin); int T; int cas = 1; cin>>T; while(T--){ for(int i = 0; i < 26; i++) scanf("%d",&w[i]); scanf("%s",t); longest(t); int n = strlen(s); sum[0] = w[s[0]-‘a‘]; for(int i = 1; i < n; i++){ if(s[i]==‘*‘) sum[i] = sum[i-1]+0; else sum[i] = sum[i-1]+w[s[i]-‘a‘]; } memset(l, 0, sizeof l); memset(r, 0, sizeof r); for(int i = 0; i < n; i++){ int L = i-(f[i]-1)/2; int R = i+(f[i]-1)/2; if(L==0){ l[R] = 1; } if(R==n-1){ r[L] = 1; } } int ans = -INF, ansa, ansb; for(int i = 1; i < n-1; i++){ if(i%2){ if(l[i-1]){ ansa = sum[i-1]; }else ansa = 0; if(r[i+1]){ ansb = sum[n-1]-sum[i-1]; }else ansb = 0; }else { if(l[i-2]){ ansa = sum[i-1]; }else ansa = 0; if(r[i]){ ansb = sum[n-1]-sum[i-1]; }else ansb = 0; } ans = max(ans,ansa+ansb); } printf("%d\n",ans); } return 0; }
/** 题目:hdu3613 Best Reward 链接:http://acm.hdu.edu.cn/showproblem.php?pid=3613 题意:有一个字符串,把他切成两部分。 如果这部分是回文串,那么他的值为所有字母的权值和。否则这部分值为0;这两部分的值和为该切法的权值。 求最大的切法的权值。 思路: 如果能够判断[0,i],[i,n-1]是一个回文串(0<=i<n)那么就可以枚举i,计算切割位置为i时候两部分的贡献和。 取最大的。 扩展kmp的做法。 先计算l[i]=1,表示[0,i]是一个回文串;那么[0,k1]与[k2,i]相等。0是固定的。 获得原串t的反转串s。求extend[i],表示s串从[i,n-1]与原串t的最长公共前缀。 枚举s串的i。那么从[i,n-1]的长度n-i;如果extend[i]*2+1>=n-i那么表示原串t中的[0,n-1-i]是一个回文串即l[n-1-i] = 1; 现在求r[i]=1,表示[i,n-1]是一个回文串;那么[i,k1]与[k2,n-1]相等。n-1是固定的。 求extend[i],表示t串从[i,n-1]与s串的最长公共前缀。 枚举t串的i。那么从[i,n-1]的长度为n-i;如果extend[i]*2+1>=n-i那么表示原串[i,n-1]是一个回文串即r[i] = 1; */ #include <cstdio> #include <cstring> #include <algorithm> #include <set> #include <iostream> #include <vector> using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 5e5+10; char s[maxn], t[maxn]; int f[maxn], Next[maxn], extend[maxn]; int sum[maxn], w[30]; int l[maxn], r[maxn];///l[i]=1表示[0,i]是一个回文串.r[i]=1表示[i,n-1]是一个回文串。 void GetNext(char *T,int* next) { int a=0; int Tlen=strlen(T); next[0]=Tlen; while(a<Tlen-1&&T[a]==T[a+1]) a++; next[1]=a; a=1; for(int k=2;k<Tlen;k++) { int p=a+next[a]-1,L=next[k-a]; if((k-1)+L>=p) { int j=(p-k+1)>0? p-k+1:0; while(k+j<Tlen&&T[k+j]==T[j]) j++; next[k]=j; a=k; } else next[k]=L; } } void GetExtend(char *S,char *T,int* next,int* extend) { int a=0; GetNext(T,next); int Slen=strlen(S); int Tlen=strlen(T); int MinLen=Slen<Tlen? Slen:Tlen; 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=next[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 getlr(int* extend,int *l,int flag,int n) { int len; for(int i = 0; i < n; i++){ len = n-i; if(extend[i]*2+1>=len){ if(flag) l[i] = 1; else l[n-1-i] = 1; } } } void test(int *f,int n) { for(int i = 0; i < n; i++){ printf("f[%d] = %d\n",i,f[i]); } } int main() { //freopen("in.txt","r",stdin); int T; cin>>T; while(T--){ for(int i = 0; i < 26; i++) scanf("%d",&w[i]); scanf("%s",t); int n = strlen(t); for(int i = 0; i < n; i++) s[n-1-i] = t[i]; s[n] = ‘\0‘; sum[0] = w[t[0]-‘a‘]; for(int i = 1; i < n; i++){ sum[i] = sum[i-1]+w[t[i]-‘a‘]; } memset(l, 0, sizeof l); memset(r, 0, sizeof r); GetExtend(s,t,Next,extend); getlr(extend,l,0,n); GetExtend(t,s,Next,extend); getlr(extend,r,1,n); int ans = -INF, ansa, ansb; for(int i = 1; i < n; i++){ if(l[i-1]){ ansa = sum[i-1]; }else ansa = 0; if(r[i]){ ansb = sum[n-1]-sum[i-1]; }else ansb = 0; ans = max(ans,ansa+ansb); } printf("%d\n",ans); } return 0; }
以上是关于hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串的主要内容,如果未能解决你的问题,请参考以下文章
kuangbin专题十六 KMP&&扩展KMP HDU3613 Best Reward(前缀和+manacher or ekmp)
hdu3613 Best Reward 扩展kmp or O(n)求最大回文子串
HDU 3613 Best Reward(拓展KMP算法求解)