链接: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
",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 */