鏈接:https://ac.nowcoder.com/acm/problem/17062
來源:牛客網
題目描述:
字符串 S 只包含小寫英文字母。有四種操作,每次操作你可以選擇其中一種:
刪除字符串的第一個字母。
刪除字符串的最后一個字母。
在字符串的頭部添加任意一個你想要的字母。
在字符串的尾部添加任意一個你想要的字母。
刪除一個第 i 種英文字母需要的花費是 Ai,添加一個第 i 種英文字母的花費是 Bi。
請問將字符串 S 變成回文串需要的最小花費是多少?
在字符串的頭部添加任意一個你想要的字母。
在字符串的尾部添加任意一個你想要的字母。
刪除一個第 i 種英文字母需要的花費是 Ai,添加一個第 i 種英文字母的花費是 Bi。
請問將字符串 S 變成回文串需要的最小花費是多少?
輸出描述:
輸出一個正整數,表示把字符串 S 變成一個回文串的最小花費。
具體思路:
首先分析操作類型,只能在頭部和尾部進行操作,換個方法也就是說只能對這個串的前綴和后綴進行操作。
所以對於每一個位置,找出以當前這個位置的最長回文串,然后這個回文串區間我們就停止操作了。開始對1~l-1和 r+1~len這兩段區間進行操作使得整個串變成回文串。
然后再繼續往下想,對於每一個前綴(1 ~ l-1),我們可以把它全部刪除;也可以刪除一部分,在字符串的另一側再填上未刪除的部分,使得總體在回文串的基礎上再構成一個回文串,后綴同理。也就是說對於每一次的操作,我們是將這個回文串的一側刪除,然后通過另一側的情況來調整被刪除的回文串一側。
按照這個思路,我們可以統計一下對於當前這個前綴,從哪里開始刪除,是最節省的,並且保存一下最小花費。后綴做同樣的處理,然后每一次枚舉這個回文串的位置就好了。
AC代碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define inf 0x3f3f3f3f 4 # define ll_inf (1ll<<60) 5 # define ll long long 6 const int maxn = 2e5+100; 7 char str[maxn],A[maxn<<1]; 8 int pre[maxn<<1],B[maxn<<1]; 9 ll del[30],add[30]; 10 void manacher(char s[],int len){ 11 //cout<<s+1<<endl; 12 int l=0; 13 A[l++]='$'; 14 A[l++]='#'; 15 for(int i=0;i<len;i++){A[l++]=s[i+1];A[l++]='#';} 16 A[l]=0; 17 //cout<<A+1<<endl; 18 int mx=0,id=0; 19 for(int i=0;i<l;i++){ 20 B[i]= mx > i ? min(B[2*id-i],mx-i):1; 21 while(A[i+B[i]]==A[i-B[i]])B[i]++; 22 if(i+B[i]>mx){ 23 mx=i+B[i]; 24 id=i; 25 } 26 } 27 } 28 ll pre_del[maxn],pre_add[maxn];// 將當前前綴刪除的花費;將當前前綴增加的花費 29 ll suf_del[maxn],suf_add[maxn];// 將當前后綴刪除的花費;將當前后綴增加上的花費 30 ll pre_cost[maxn],suf_cost[maxn]; // 對於當前的前綴,調整這一段,使得整個字符串前綴不影響構成回文串的最小花費,后綴同理 31 void init(int len){ 32 //pre_del[0]=del[str[0]-'a']; 33 for(int i=1;i<=len;i++){pre_del[i]=pre_del[i-1]+del[str[i]-'a'];} 34 //pre_add[1]=add[str[1]-'a']; 35 for(int i=1;i<=len;i++){pre_add[i]=pre_add[i-1]+add[str[i]-'a'];} 36 //suf_del[len-1]=del[str[len-1]-'a']; 37 for(int i=len;i>=1;i--){suf_del[i]=suf_del[i+1]+del[str[i]-'a'];} 38 //suf_add[len-1]=add[str[len-1]-'a']; 39 for(int i=len;i>=1;i--){suf_add[i]=suf_add[i+1]+add[str[i]-'a'];} 40 41 int pos=0; 42 for(int i=1;i<=len;i++){ 43 if(pre_del[pos]+pre_add[i]-pre_add[pos]<pre_del[i]){ 44 pre_cost[i]=pre_del[pos]+pre_add[i]-pre_add[pos]; 45 } 46 else {pos=i;pre_cost[i]=pre_del[i];} 47 } 48 pos=len+1; 49 for(int i=len;i>=1;i--){ 50 if(suf_del[pos]+suf_add[i]-suf_add[pos]<suf_del[i]){ 51 suf_cost[i]=suf_del[pos]+suf_add[i]-suf_add[pos]; 52 } 53 else {pos=i;suf_cost[i]=suf_del[i];} 54 55 } 56 } 57 int p[maxn]; 58 int main(){ 59 scanf("%s",str+1); 60 int len=strlen(str+1);// 本來下標從0開始,發現求pre_cost等數組的時候打起來比較麻煩; 61 for(int i=0;i<26;i++){ 62 scanf("%lld %lld",&del[i],&add[i]); 63 } 64 init(len); 65 //cout<<str+1<<endl; 66 manacher(str,len); 67 ll minn=ll_inf; 68 //for(int i=0;i<len+len;i++){ 69 //cout<<A[i]<<" "<<B[i]<<endl; 70 //} 71 //cout<<pre_del[len]<<endl; 72 int l,r; 73 for(int i=1;i<=len+len+2;i++){ 74 if((i&1)){ 75 l=i/2-(B[i]-1)/2;// 之所以是i/2的原因,這個是對於新構成的回文串來說的,長度比原來增加的兩倍多 76 r=i/2+(B[i]-1)/2+1; 77 } 78 else { 79 l=i/2-(B[i]-1)/2-1; 80 r=i/2+(B[i]-1)/2+1; 81 } 82 minn=min(minn,min(pre_del[l]+suf_cost[r],suf_del[r]+pre_cost[l])); 83 } 84 printf("%lld\n",minn); 85 return 0; 86 } 87 /* 88 jell 89 1000 1100 90 350 700 91 200 800 92 2000 2000 93 2000 432 94 2000 2000 95 2000 2000 96 2000 2000 97 2000 2000 98 20 2000 99 2000 2000 100 350 35 101 200 800 102 2000 2000 103 2000 2000 104 2000 2000 105 2000 2000 106 2000 2000 107 2000 2000 108 2000 2000 109 2000 2000 110 2000 2000 111 2000 2000 112 2000 2000 113 15 2000 114 2000 2000 115 */