HDU 3613 Best Reward(exkmp)
阿新 • • 發佈:2018-11-04
題意
首先給你一個長度為26的陣列,表示的是26個字母代表的權值,之後給你一串字串,之後給你一串字元,你需要將這個字元變成切成兩半,如果你切下來的部分是迴文串的話,那麼你將得到這部分的權值,如果他不是迴文串的話他的權值就是0,現在問你怎樣切使得切下來的兩個串的權值最大。
思路
首先將原串
翻轉出來變成
,這個時候你會發現,如果一個串是迴文串的話,原串翻轉出來的串和原串其實相等的,那麼他切成兩個部分,就會有原串的字首其實是後來串的字尾,那麼就把他傳化成了一個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;
}
}