BZOJ 3325 [SCOI2013]密码 (逆模拟Manacher+构造)

题目大意:给你一个字符串每个位置和相邻两个位置为回文中心的最长回文串长度,让你构造一个合法的字典序最小的字符串

挺有意思的构造题

首先按照$Manacher$的思想还原$p$数组

定义$f_{ij}$表示$i$位置不能放$j$这个字符

我们逆模拟$manacher$的构造过程,如果$i+p_{i}>maxR$,那么多出来这部分都要满足回文性质,然后更新$maxR$

然后,因为回文不能继续扩展,所以$str[i+p_{i}]!=str[i-p_{i}]$,就在f数组里打上标记

每遍历到一个位置,如果这个位置还没有被“安排”上一个字符,就依照$f$数组,贪心取能取的最小的即可

 1 #include <cmath>
 2 #include <queue>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define NN 2001
 8 #define MM 1510
 9 #define ll long long
10 #define dd double  
11 #define uint unsigned int
12 #define mod 1000000007
13 using namespace std;
14 
15 int n,nn;
16 int a[NN],b[NN],nt[NN][26];
17 int ans[NN],p[NN];
18 
19 int main()
20 {
21     scanf("%d",&n);nn=2*n+1;
22     for(int i=2;i<=nn;i+=2)
23         scanf("%d",&a[i]),p[i]=a[i]+1;
24     for(int i=3;i<nn;i+=2)
25         scanf("%d",&b[i]),p[i]=b[i]+1;
26     int mr=2+p[2],mid=2;
27     ans[2]=1;
28     for(int i=3;i<=nn;i++){
29         if(i+p[i]>mr){
30             for(int j=mr;j<i+p[i];j++)
31                 ans[j]=ans[2*i-j];
32             mr=i+p[i],mid=i;
33         }
34         nt[i+p[i]][ans[i-p[i]]]=1;
35         if(i%2==0&&!ans[i]) 
36             for(int j=1;j<=26;j++)
37             if(!nt[i][j]){
38                 ans[i]=j;
39                 break;
40             }
41     }
42     for(int i=2;i<=nn;i+=2)
43         printf("%c",ans[i]+'a'-1);
44     puts("");
45     return 0;
46 }

 

posted @ 2018-11-26 18:55  guapisolo  阅读(93)  评论(0编辑  收藏