1. 程式人生 > >HDU 3613 Best Reward(exkmp)

HDU 3613 Best Reward(exkmp)

題意
首先給你一個長度為26的陣列,表示的是26個字母代表的權值,之後給你一串字串,之後給你一串字元,你需要將這個字元變成切成兩半,如果你切下來的部分是迴文串的話,那麼你將得到這部分的權值,如果他不是迴文串的話他的權值就是0,現在問你怎樣切使得切下來的兩個串的權值最大。
思路
首先將原串 c h ch 翻轉出來變成 s

h sh ,這個時候你會發現,如果一個串是迴文串的話,原串翻轉出來的串和原串其實相等的,那麼他切成兩個部分,就會有原串的字首其實是後來串的字尾,那麼就把他傳化成了一個exkmp了,我們將原串和翻轉串都做一次exkmp,之後列舉切的位置,之後貪心的去取就好了。
程式碼

#include <bits/stdc++.h>
using namespace std;
const int maxn = 500000 + 10;
int Rank[30] , nex[
maxn] , ex1[maxn] , sum[maxn] ,ex2[maxn]; void EKMP(string s,string t,int ex[])//s[]為主串,t[]為模式串 { int i,j,p,l; int len=t.size(); int len1=s.size(); memset(nex,0,sizeof(nex)); memset(ex,0,sizeof(ex)); nex[0] = len; j = 0; while(j+1 < len && t[j] == t[1+j])j++; nex[
1]=j; int a=1; for(int i=2;i<len;i++) { p = nex[a]+a-1; l = nex[i-a]; if(i + l < p + 1 )nex[i]=l; else { j=max(0,p-i+1); while(i+j<len&&t[i+j]==t[0+j])j++; nex[i]=j; a=i; } } j = 0; while(j < len1 && j < len && s[j] == t[j])j++; ex[0]=j; a=0; for(i = 1;i < len1;i++) { p = ex[a]+a-1; l = nex[i-a]; if(l + i < p + 1)ex[i] = l; else { j = max(0,p-i+1); while(i + j < len1 && j < len && s[i+j] == t[j])j++; ex[i]=j; a=i; } } } int main() { int t; scanf("%d",&t); while(t--) { memset(sum,0,sizeof(sum)); string ch , sh; for(int i = 0 ; i < 26 ; i++) scanf("%d",&Rank[i]); cin>>ch; sum[0] = 0; for(int i = 0 ; i < ch.size() ; i ++) sum[i+1] = sum[i] + Rank[ch[i]-'a']; sh = ch; reverse(sh.begin(),sh.end()); EKMP(ch,sh,ex1); EKMP(sh,ch,ex2); int MAX = -1 , ans = 0 ; for(int i = 1 ; i < sh.size() ; i++) { ans = 0; int j = sh.size() - i; if(i + ex1[i] == sh.size()) ans += sum[sh.size()] - sum[i]; // 首先判斷[i~n]的位置是不是迴文的。 if(j + ex2[j] == sh.size()) ans += sum[i]; // 然後去另外一邊觀察[0~i]的位置 MAX = max(MAX,ans); } cout<<MAX<<endl; } }